diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2d1992dc43..9e09dc21074 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,12 @@ on: schedule: - cron: "22 4 * * *" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name == 'schedule' }} + cancel-in-progress: true + +permissions: {} + jobs: build: if: ${{ always() }} @@ -25,6 +31,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Cache west modules uses: actions/cache@v4 env: @@ -131,7 +139,7 @@ jobs: throw new Error('Failed to build one or more configurations'); } compile-matrix: - if: ${{ always() }} + if: ${{ !cancelled() }} runs-on: ubuntu-latest needs: [core-coverage, board-changes, nightly] outputs: @@ -179,6 +187,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Use Node.js uses: actions/setup-node@v4 with: @@ -284,7 +294,7 @@ jobs: }); }))).flat(); nightly: - if: ${{ github.event_name == 'schedule' }} + if: ${{ github.event_name == 'schedule' && github.repository_owner == 'zmkfirmware' }} runs-on: ubuntu-latest needs: get-grouped-hardware outputs: @@ -335,6 +345,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Use Node.js uses: actions/setup-node@v4 with: @@ -413,7 +425,11 @@ jobs: board-changes: ${{ steps.board-changes.outputs.result }} core-changes: ${{ steps.core-changes.outputs.result }} steps: - - uses: tj-actions/changed-files@v42 + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: tj-actions/changed-files@v44 id: changed-files with: json: true diff --git a/app/Kconfig b/app/Kconfig index 35e05a580ff..a735ff58b05 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -113,6 +113,12 @@ config ZMK_HID_INDICATORS Enable HID indicators, used for detecting state of Caps/Scroll/Num Lock, Kata, and Compose. +config ZMK_HID_SEPARATE_MOD_RELEASE_REPORT + bool "Release Modifiers Separately" + help + Send a separate release event for the modifiers, to make sure the release + of the modifier doesn't get recognized before the actual key's release event. + menu "Output Types" config ZMK_USB @@ -490,7 +496,11 @@ if USB_DEVICE_STACK config ZMK_USB_INIT_PRIORITY int "USB Init Priority" - default 50 + default 94 + +config ZMK_USB_HID_INIT_PRIORITY + int "USB HID Init Priority" + default 95 #USB endif diff --git a/app/Kconfig.behaviors b/app/Kconfig.behaviors index c9754bf7d83..c6cc45f3b87 100644 --- a/app/Kconfig.behaviors +++ b/app/Kconfig.behaviors @@ -1,6 +1,12 @@ # Copyright (c) 2023 The ZMK Contributors # SPDX-License-Identifier: MIT +config ZMK_BEHAVIOR_METADATA + bool "Metadata" + help + Enabling this option adds APIs for documenting and fetching + metadata describing a behaviors name, and supported parameters. + config ZMK_BEHAVIOR_KEY_TOGGLE bool default y @@ -35,4 +41,4 @@ config ZMK_BEHAVIOR_SENSOR_ROTATE_VAR config ZMK_BEHAVIOR_MACRO bool default y - depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED \ No newline at end of file + depends on DT_HAS_ZMK_BEHAVIOR_MACRO_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_ONE_PARAM_ENABLED || DT_HAS_ZMK_BEHAVIOR_MACRO_TWO_PARAM_ENABLED diff --git a/app/boards/01space_rp2040_042lcd.overlay b/app/boards/01space_rp2040_042lcd.overlay index d89e53f4a8e..b5d2cdb2fd1 100644 --- a/app/boards/01space_rp2040_042lcd.overlay +++ b/app/boards/01space_rp2040_042lcd.overlay @@ -4,6 +4,4 @@ * SPDX-License-Identifier: MIT */ -#include "usb_console.dtsi" - &xiao_serial { status = "disabled"; }; diff --git a/app/boards/adafruit_kb2040.overlay b/app/boards/adafruit_kb2040.overlay index b14e0d04d47..72b3adcaf41 100644 --- a/app/boards/adafruit_kb2040.overlay +++ b/app/boards/adafruit_kb2040.overlay @@ -4,6 +4,4 @@ * SPDX-License-Identifier: MIT */ -#include "usb_console.dtsi" - &pro_micro_serial { status = "disabled"; }; diff --git a/app/boards/adafruit_qt_py_rp2040.overlay b/app/boards/adafruit_qt_py_rp2040.overlay index d89e53f4a8e..b5d2cdb2fd1 100644 --- a/app/boards/adafruit_qt_py_rp2040.overlay +++ b/app/boards/adafruit_qt_py_rp2040.overlay @@ -4,6 +4,4 @@ * SPDX-License-Identifier: MIT */ -#include "usb_console.dtsi" - &xiao_serial { status = "disabled"; }; diff --git a/app/boards/arm/adv360pro/adv360pro.dtsi b/app/boards/arm/adv360pro/adv360pro.dtsi index c64d0d3f77e..ea68624b981 100644 --- a/app/boards/arm/adv360pro/adv360pro.dtsi +++ b/app/boards/arm/adv360pro/adv360pro.dtsi @@ -21,7 +21,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,kscan = &kscan0; zmk,backlight = &backlight; zmk,battery = &vbatt; @@ -90,11 +89,8 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &flash0 { diff --git a/app/boards/arm/bdn9/bdn9_rev2.dts b/app/boards/arm/bdn9/bdn9_rev2.dts index 6e15408a2cd..2189530d4d2 100644 --- a/app/boards/arm/bdn9/bdn9_rev2.dts +++ b/app/boards/arm/bdn9/bdn9_rev2.dts @@ -16,7 +16,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,kscan = &kscan; zmk,underglow = &led_strip; }; @@ -106,13 +105,10 @@ apb1-prescaler = <1>; }; -&usb { +zephyr_udc0: &usb { status = "okay"; pinctrl-0 = <&usb_dm_pa11 &usb_dp_pa12>; pinctrl-names = "default"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &rtc { diff --git a/app/boards/arm/bluemicro840/bluemicro840_v1.dts b/app/boards/arm/bluemicro840/bluemicro840_v1.dts index aabdf310a28..84d3ebaec99 100644 --- a/app/boards/arm/bluemicro840/bluemicro840_v1.dts +++ b/app/boards/arm/bluemicro840/bluemicro840_v1.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -82,11 +81,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/bt60/bt60.dtsi b/app/boards/arm/bt60/bt60.dtsi index 655d25769a7..83ff3f04aa9 100644 --- a/app/boards/arm/bt60/bt60.dtsi +++ b/app/boards/arm/bt60/bt60.dtsi @@ -16,7 +16,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; zmk,kscan = &kscan0; zmk,matrix-transform = &default_transform; @@ -70,11 +69,8 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/ckp/ckp.dtsi b/app/boards/arm/ckp/ckp.dtsi index 4142622ac86..b127cabc71a 100644 --- a/app/boards/arm/ckp/ckp.dtsi +++ b/app/boards/arm/ckp/ckp.dtsi @@ -142,7 +142,7 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; }; diff --git a/app/boards/arm/corneish_zen/corneish_zen.dtsi b/app/boards/arm/corneish_zen/corneish_zen.dtsi index 881fadb0764..dbd6f93e805 100644 --- a/app/boards/arm/corneish_zen/corneish_zen.dtsi +++ b/app/boards/arm/corneish_zen/corneish_zen.dtsi @@ -20,7 +20,6 @@ zephyr,flash = &flash0; zmk,kscan = &kscan0; zmk,display = &epd; - zephyr,console = &cdc_acm_uart; zmk,matrix-transform = &default_transform; }; @@ -76,11 +75,8 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &flash0 { diff --git a/app/boards/arm/dz60rgb/dz60rgb_rev1.dts b/app/boards/arm/dz60rgb/dz60rgb_rev1.dts index 4e1d4b66579..b8fac4e2807 100644 --- a/app/boards/arm/dz60rgb/dz60rgb_rev1.dts +++ b/app/boards/arm/dz60rgb/dz60rgb_rev1.dts @@ -16,7 +16,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,kscan = &kscan0; zmk,matrix-transform = &default_transform; }; @@ -65,11 +64,8 @@ RC(4,0) RC(4,1) RC(4,2) RC(4,5) RC( }; -&usb { +zephyr_udc0: &usb { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &flash0 { diff --git a/app/boards/arm/ferris/ferris_rev02.dts b/app/boards/arm/ferris/ferris_rev02.dts index 4fecd280eec..a0e28f03833 100644 --- a/app/boards/arm/ferris/ferris_rev02.dts +++ b/app/boards/arm/ferris/ferris_rev02.dts @@ -17,7 +17,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,kscan = &kscan; zmk,matrix-transform = &transform; /* TODO: Enable once we support the IC for underglow @@ -110,14 +109,11 @@ }; }; -&usb { +zephyr_udc0: &usb { status = "okay"; pinctrl-0 = <&usb_dm_pa11 &usb_dp_pa12>; pinctrl-names = "default"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &clk_hsi { diff --git a/app/boards/arm/glove80/glove80.dtsi b/app/boards/arm/glove80/glove80.dtsi index 4803488b078..d51a73ac066 100644 --- a/app/boards/arm/glove80/glove80.dtsi +++ b/app/boards/arm/glove80/glove80.dtsi @@ -15,7 +15,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; }; default_transform: keymap_transform_0 { @@ -59,11 +58,8 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &flash0 { diff --git a/app/boards/arm/kbdfans_tofu65/kbdfans_tofu65_v2.dts b/app/boards/arm/kbdfans_tofu65/kbdfans_tofu65_v2.dts index 18c92671d94..60ba1da0143 100644 --- a/app/boards/arm/kbdfans_tofu65/kbdfans_tofu65_v2.dts +++ b/app/boards/arm/kbdfans_tofu65/kbdfans_tofu65_v2.dts @@ -13,8 +13,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; - zephyr,shell-uart = &cdc_acm_uart; zephyr,code-partition = &code_partition; zmk,kscan = &kscan0; zmk,matrix-transform = &default_transform; @@ -108,11 +106,8 @@ RC(4,0) RC(4,1) RC(4,2) RC(4,6) RC(4,8) RC(4,9) }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/mikoto/mikoto_520.dts b/app/boards/arm/mikoto/mikoto_520.dts index a6ca50812f8..3ea48cd9911 100644 --- a/app/boards/arm/mikoto/mikoto_520.dts +++ b/app/boards/arm/mikoto/mikoto_520.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -81,11 +80,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/nice60/nice60.dts b/app/boards/arm/nice60/nice60.dts index d1b9f9927cc..4eefbb9d8f3 100644 --- a/app/boards/arm/nice60/nice60.dts +++ b/app/boards/arm/nice60/nice60.dts @@ -20,7 +20,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; zmk,kscan = &kscan0; zmk,matrix-transform = &default_transform; @@ -129,11 +128,8 @@ RC(4,0) RC(4,1) RC(4,2) RC(4,5) R }; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &flash0 { diff --git a/app/boards/arm/nice_nano/nice_nano.dtsi b/app/boards/arm/nice_nano/nice_nano.dtsi index 41770dd310e..839845c8e8c 100644 --- a/app/boards/arm/nice_nano/nice_nano.dtsi +++ b/app/boards/arm/nice_nano/nice_nano.dtsi @@ -16,7 +16,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; }; leds { @@ -65,11 +64,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/nrf52840_m2/nrf52840_m2.dts b/app/boards/arm/nrf52840_m2/nrf52840_m2.dts index 85e9ce2107f..39569f0b760 100644 --- a/app/boards/arm/nrf52840_m2/nrf52840_m2.dts +++ b/app/boards/arm/nrf52840_m2/nrf52840_m2.dts @@ -15,7 +15,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -57,12 +56,9 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { compatible = "nordic,nrf-usbd"; status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/nrfmicro/nrfmicro_11.dts b/app/boards/arm/nrfmicro/nrfmicro_11.dts index 04368ab8741..b80ed4c62c1 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_11.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_11.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; }; leds { @@ -69,11 +68,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts b/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts index 600935aa0a6..7b89b62f88a 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; }; leds { @@ -69,11 +68,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/nrfmicro/nrfmicro_13.dts b/app/boards/arm/nrfmicro/nrfmicro_13.dts index 716e5b181dd..0cb22e63ca9 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_13.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_13.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -81,11 +80,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts b/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts index f57c413da8b..866276bbec4 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -81,11 +80,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/pillbug/pillbug.dts b/app/boards/arm/pillbug/pillbug.dts index c30d306e275..cf4f62fc98b 100644 --- a/app/boards/arm/pillbug/pillbug.dts +++ b/app/boards/arm/pillbug/pillbug.dts @@ -18,7 +18,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -83,11 +82,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/planck/planck_rev6.dts b/app/boards/arm/planck/planck_rev6.dts index 5b8e16b2efe..85b751400a1 100644 --- a/app/boards/arm/planck/planck_rev6.dts +++ b/app/boards/arm/planck/planck_rev6.dts @@ -16,7 +16,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,kscan = &kscan0; zmk,matrix-transform = &layout_grid_transform; }; @@ -96,13 +95,10 @@ layout_2x2u_transform: }; }; -&usb { +zephyr_udc0: &usb { pinctrl-0 = <&usb_dm_pa11 &usb_dp_pa12>; pinctrl-names = "default"; status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &clk_hse { diff --git a/app/boards/arm/preonic/preonic_rev3.dts b/app/boards/arm/preonic/preonic_rev3.dts index d14355dac93..79f88c33b0a 100644 --- a/app/boards/arm/preonic/preonic_rev3.dts +++ b/app/boards/arm/preonic/preonic_rev3.dts @@ -17,7 +17,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,kscan = &kscan0; zmk,matrix-transform = &layout_grid_transform; }; @@ -90,13 +89,10 @@ }; }; -&usb { +zephyr_udc0: &usb { pinctrl-0 = <&usb_dm_pa11 &usb_dp_pa12>; pinctrl-names = "default"; status = "okay"; -cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &clk_hse { diff --git a/app/boards/arm/proton_c/proton_c.dts b/app/boards/arm/proton_c/proton_c.dts index 3aad62c8f30..05872b25dde 100644 --- a/app/boards/arm/proton_c/proton_c.dts +++ b/app/boards/arm/proton_c/proton_c.dts @@ -16,7 +16,6 @@ chosen { zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart0; }; aliases { @@ -66,13 +65,10 @@ apb2-prescaler = <1>; }; -&usb { +zephyr_udc0: &usb { pinctrl-0 = <&usb_dm_pa11 &usb_dp_pa12>; pinctrl-names = "default"; status = "okay"; - cdc_acm_uart0: cdc_acm_uart0 { - compatible = "zephyr,cdc-acm-uart"; - }; }; &rtc { diff --git a/app/boards/arm/puchi_ble/puchi_ble_v1.dts b/app/boards/arm/puchi_ble/puchi_ble_v1.dts index 05aba8d3780..9f3194e036f 100644 --- a/app/boards/arm/puchi_ble/puchi_ble_v1.dts +++ b/app/boards/arm/puchi_ble/puchi_ble_v1.dts @@ -17,7 +17,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -72,11 +71,8 @@ pinctrl-names = "default", "sleep"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; diff --git a/app/boards/arm/s40nc/s40nc.dts b/app/boards/arm/s40nc/s40nc.dts index a2eb89ea207..4c37030db33 100644 --- a/app/boards/arm/s40nc/s40nc.dts +++ b/app/boards/arm/s40nc/s40nc.dts @@ -16,7 +16,6 @@ zephyr,code-partition = &code_partition; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; zmk,kscan = &kscan0; zmk,matrix-transform = &default_transform; @@ -93,11 +92,8 @@ status = "okay"; }; -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; &flash0 { diff --git a/app/boards/boardsource_blok.overlay b/app/boards/boardsource_blok.overlay index b14e0d04d47..72b3adcaf41 100644 --- a/app/boards/boardsource_blok.overlay +++ b/app/boards/boardsource_blok.overlay @@ -4,6 +4,4 @@ * SPDX-License-Identifier: MIT */ -#include "usb_console.dtsi" - &pro_micro_serial { status = "disabled"; }; diff --git a/app/boards/rpi_pico.overlay b/app/boards/rpi_pico.overlay deleted file mode 100644 index efc8e080f15..00000000000 --- a/app/boards/rpi_pico.overlay +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (c) 2023 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include "usb_console.dtsi" - diff --git a/app/boards/seeeduino_xiao.overlay b/app/boards/seeeduino_xiao.overlay deleted file mode 100644 index 285ee4de332..00000000000 --- a/app/boards/seeeduino_xiao.overlay +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2022 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -/ { - chosen { - zephyr,console = &cdc_acm_uart; - }; -}; - -&usb0 { - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; -}; - diff --git a/app/boards/seeeduino_xiao_ble.overlay b/app/boards/seeeduino_xiao_ble.overlay index e0934691d84..f6a608582e6 100644 --- a/app/boards/seeeduino_xiao_ble.overlay +++ b/app/boards/seeeduino_xiao_ble.overlay @@ -7,7 +7,6 @@ / { chosen { - zephyr,console = &cdc_acm_uart; zmk,battery = &vbatt; }; @@ -24,12 +23,6 @@ status = "okay"; }; -&usbd { - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; -}; - &qspi { status = "okay"; pinctrl-0 = <&qspi_default>; diff --git a/app/boards/seeeduino_xiao_rp2040.overlay b/app/boards/seeeduino_xiao_rp2040.overlay index d89e53f4a8e..b5d2cdb2fd1 100644 --- a/app/boards/seeeduino_xiao_rp2040.overlay +++ b/app/boards/seeeduino_xiao_rp2040.overlay @@ -4,6 +4,4 @@ * SPDX-License-Identifier: MIT */ -#include "usb_console.dtsi" - &xiao_serial { status = "disabled"; }; diff --git a/app/boards/sparkfun_pro_micro_rp2040.overlay b/app/boards/sparkfun_pro_micro_rp2040.overlay index b14e0d04d47..72b3adcaf41 100644 --- a/app/boards/sparkfun_pro_micro_rp2040.overlay +++ b/app/boards/sparkfun_pro_micro_rp2040.overlay @@ -4,6 +4,4 @@ * SPDX-License-Identifier: MIT */ -#include "usb_console.dtsi" - &pro_micro_serial { status = "disabled"; }; diff --git a/app/boards/usb_console.dtsi b/app/boards/usb_console.dtsi deleted file mode 100644 index adf3bd19bf1..00000000000 --- a/app/boards/usb_console.dtsi +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2023 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - - -/ { - chosen { - zephyr,console = &cdc_acm_uart; - }; -}; - -&usbd { - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; -}; - diff --git a/app/core-coverage.yml b/app/core-coverage.yml index 4a60aad9caf..7eef35cf70f 100644 --- a/app/core-coverage.yml +++ b/app/core-coverage.yml @@ -22,6 +22,9 @@ include: shield: kyria_left cmake-args: "-DCONFIG_ZMK_DISPLAY=y" nickname: "display" + - board: sparkfun_pro_micro_rp2040 + shield: reviung41 + cmake-args: "-DSNIPPET='zmk-usb-logging'" - board: nice_nano_v2 shield: kyria_right cmake-args: "-DCONFIG_ZMK_DISPLAY=y" diff --git a/app/dts/behaviors/backlight.dtsi b/app/dts/behaviors/backlight.dtsi index 54c83ff44c1..dd045effed3 100644 --- a/app/dts/behaviors/backlight.dtsi +++ b/app/dts/behaviors/backlight.dtsi @@ -10,6 +10,7 @@ /omit-if-no-ref/ bl: bcklight { compatible = "zmk,behavior-backlight"; #binding-cells = <2>; + display-name = "Backlight"; }; }; }; diff --git a/app/dts/behaviors/bluetooth.dtsi b/app/dts/behaviors/bluetooth.dtsi index 40557b7a28c..bece156f8fb 100644 --- a/app/dts/behaviors/bluetooth.dtsi +++ b/app/dts/behaviors/bluetooth.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ bt: bluetooth { compatible = "zmk,behavior-bluetooth"; #binding-cells = <2>; + display-name = "Bluetooth"; }; }; }; diff --git a/app/dts/behaviors/caps_word.dtsi b/app/dts/behaviors/caps_word.dtsi index 795fbc08439..05431bd8db2 100644 --- a/app/dts/behaviors/caps_word.dtsi +++ b/app/dts/behaviors/caps_word.dtsi @@ -12,6 +12,7 @@ compatible = "zmk,behavior-caps-word"; #binding-cells = <0>; continue-list = ; + display-name = "Caps Word"; }; }; }; diff --git a/app/dts/behaviors/ext_power.dtsi b/app/dts/behaviors/ext_power.dtsi index 2ae1daf84a8..08113f94bbe 100644 --- a/app/dts/behaviors/ext_power.dtsi +++ b/app/dts/behaviors/ext_power.dtsi @@ -10,6 +10,7 @@ ext_power: extpower { compatible = "zmk,behavior-ext-power"; #binding-cells = <1>; + display-name = "External Power"; }; }; }; diff --git a/app/dts/behaviors/gresc.dtsi b/app/dts/behaviors/gresc.dtsi index 59a7329178f..2643a383d81 100644 --- a/app/dts/behaviors/gresc.dtsi +++ b/app/dts/behaviors/gresc.dtsi @@ -13,6 +13,7 @@ #binding-cells = <0>; bindings = <&kp ESC>, <&kp GRAVE>; mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; + display-name = "Grave/Escape"; }; }; }; diff --git a/app/dts/behaviors/key_press.dtsi b/app/dts/behaviors/key_press.dtsi index ddaf7eed374..2435699b6ab 100644 --- a/app/dts/behaviors/key_press.dtsi +++ b/app/dts/behaviors/key_press.dtsi @@ -10,6 +10,7 @@ /omit-if-no-ref/ cp: kp: key_press { compatible = "zmk,behavior-key-press"; #binding-cells = <1>; + display-name = "Key Press"; }; }; }; diff --git a/app/dts/behaviors/key_repeat.dtsi b/app/dts/behaviors/key_repeat.dtsi index 88910f6271c..cd5d3771dcb 100644 --- a/app/dts/behaviors/key_repeat.dtsi +++ b/app/dts/behaviors/key_repeat.dtsi @@ -12,6 +12,7 @@ compatible = "zmk,behavior-key-repeat"; #binding-cells = <0>; usage-pages = ; + display-name = "Key Repeat"; }; }; }; diff --git a/app/dts/behaviors/key_toggle.dtsi b/app/dts/behaviors/key_toggle.dtsi index a3e3f36f270..a7b66aab1af 100644 --- a/app/dts/behaviors/key_toggle.dtsi +++ b/app/dts/behaviors/key_toggle.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ kt: key_toggle { compatible = "zmk,behavior-key-toggle"; #binding-cells = <1>; + display-name = "Key Toggle"; }; }; }; diff --git a/app/dts/behaviors/layer_tap.dtsi b/app/dts/behaviors/layer_tap.dtsi index dc953e9358b..2858bf17bc5 100644 --- a/app/dts/behaviors/layer_tap.dtsi +++ b/app/dts/behaviors/layer_tap.dtsi @@ -12,6 +12,7 @@ flavor = "tap-preferred"; tapping-term-ms = <200>; bindings = <&mo>, <&kp>; + display-name = "Layer-Tap"; }; }; }; diff --git a/app/dts/behaviors/mod_tap.dtsi b/app/dts/behaviors/mod_tap.dtsi index 38bb34fe5c4..0b46f77e739 100644 --- a/app/dts/behaviors/mod_tap.dtsi +++ b/app/dts/behaviors/mod_tap.dtsi @@ -12,6 +12,7 @@ flavor = "hold-preferred"; tapping-term-ms = <200>; bindings = <&kp>, <&kp>; + display-name = "Mod-Tap"; }; }; }; diff --git a/app/dts/behaviors/momentary_layer.dtsi b/app/dts/behaviors/momentary_layer.dtsi index 6d85165dbb3..cae08d5f101 100644 --- a/app/dts/behaviors/momentary_layer.dtsi +++ b/app/dts/behaviors/momentary_layer.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ mo: momentary_layer { compatible = "zmk,behavior-momentary-layer"; #binding-cells = <1>; + display-name = "Momentary Layer"; }; }; }; diff --git a/app/dts/behaviors/none.dtsi b/app/dts/behaviors/none.dtsi index 13d056f0cf2..a9a820c30b5 100644 --- a/app/dts/behaviors/none.dtsi +++ b/app/dts/behaviors/none.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ none: none { compatible = "zmk,behavior-none"; #binding-cells = <0>; + display-name = "None"; }; }; }; diff --git a/app/dts/behaviors/outputs.dtsi b/app/dts/behaviors/outputs.dtsi index f7737196719..3047852adce 100644 --- a/app/dts/behaviors/outputs.dtsi +++ b/app/dts/behaviors/outputs.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ out: outputs { compatible = "zmk,behavior-outputs"; #binding-cells = <1>; + display-name = "Output Selection"; }; }; }; diff --git a/app/dts/behaviors/reset.dtsi b/app/dts/behaviors/reset.dtsi index e407b107b90..2aa41d7d6e8 100644 --- a/app/dts/behaviors/reset.dtsi +++ b/app/dts/behaviors/reset.dtsi @@ -12,6 +12,7 @@ sys_reset: sysreset { compatible = "zmk,behavior-reset"; #binding-cells = <0>; + display-name = "Reset"; }; // Behavior can be invoked on peripherals, so name must be <= 8 characters. @@ -19,6 +20,7 @@ compatible = "zmk,behavior-reset"; type = ; #binding-cells = <0>; + display-name = "Bootloader"; }; }; }; diff --git a/app/dts/behaviors/rgb_underglow.dtsi b/app/dts/behaviors/rgb_underglow.dtsi index 969518a6ff3..0764005872d 100644 --- a/app/dts/behaviors/rgb_underglow.dtsi +++ b/app/dts/behaviors/rgb_underglow.dtsi @@ -10,6 +10,7 @@ rgb_ug: rgb_ug { compatible = "zmk,behavior-rgb-underglow"; #binding-cells = <2>; + display-name = "Underglow"; }; }; }; diff --git a/app/dts/behaviors/sticky_key.dtsi b/app/dts/behaviors/sticky_key.dtsi index c8973d4df2c..382a7254e7b 100644 --- a/app/dts/behaviors/sticky_key.dtsi +++ b/app/dts/behaviors/sticky_key.dtsi @@ -12,6 +12,7 @@ release-after-ms = <1000>; bindings = <&kp>; ignore-modifiers; + display-name = "Sticky Key"; }; /omit-if-no-ref/ sl: sticky_layer { compatible = "zmk,behavior-sticky-key"; @@ -19,6 +20,7 @@ release-after-ms = <1000>; bindings = <&mo>; quick-release; + display-name = "Sticky Layer"; }; }; diff --git a/app/dts/behaviors/to_layer.dtsi b/app/dts/behaviors/to_layer.dtsi index 904f023da53..3c740209cb1 100644 --- a/app/dts/behaviors/to_layer.dtsi +++ b/app/dts/behaviors/to_layer.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ to: to_layer { compatible = "zmk,behavior-to-layer"; #binding-cells = <1>; + display-name = "To Layer"; }; }; }; diff --git a/app/dts/behaviors/toggle_layer.dtsi b/app/dts/behaviors/toggle_layer.dtsi index 05f2988e08c..ea9b25b7c1d 100644 --- a/app/dts/behaviors/toggle_layer.dtsi +++ b/app/dts/behaviors/toggle_layer.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ tog: toggle_layer { compatible = "zmk,behavior-toggle-layer"; #binding-cells = <1>; + display-name = "Toggle Layer"; }; }; }; diff --git a/app/dts/behaviors/transparent.dtsi b/app/dts/behaviors/transparent.dtsi index 3586f02afab..03ec36a64e8 100644 --- a/app/dts/behaviors/transparent.dtsi +++ b/app/dts/behaviors/transparent.dtsi @@ -9,6 +9,7 @@ /omit-if-no-ref/ trans: transparent { compatible = "zmk,behavior-transparent"; #binding-cells = <0>; + display-name = "Transparent"; }; }; }; diff --git a/app/dts/bindings/behaviors/behavior-metadata.yaml b/app/dts/bindings/behaviors/behavior-metadata.yaml new file mode 100644 index 00000000000..3a758ba3e22 --- /dev/null +++ b/app/dts/bindings/behaviors/behavior-metadata.yaml @@ -0,0 +1,6 @@ +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT + +properties: + display-name: + type: string diff --git a/app/dts/bindings/behaviors/one_param.yaml b/app/dts/bindings/behaviors/one_param.yaml index 9a503e8a815..fa4c2dc0b44 100644 --- a/app/dts/bindings/behaviors/one_param.yaml +++ b/app/dts/bindings/behaviors/one_param.yaml @@ -1,6 +1,8 @@ # Copyright (c) 2020 The ZMK Contributors # SPDX-License-Identifier: MIT +include: behavior-metadata.yaml + properties: label: type: string diff --git a/app/dts/bindings/behaviors/two_param.yaml b/app/dts/bindings/behaviors/two_param.yaml index 4f342301bb3..af9618e1624 100644 --- a/app/dts/bindings/behaviors/two_param.yaml +++ b/app/dts/bindings/behaviors/two_param.yaml @@ -1,6 +1,8 @@ # Copyright (c) 2020 The ZMK Contributors # SPDX-License-Identifier: MIT +include: behavior-metadata.yaml + properties: label: type: string diff --git a/app/dts/bindings/behaviors/zero_param.yaml b/app/dts/bindings/behaviors/zero_param.yaml index 79d0dcaed3f..deed5a1218b 100644 --- a/app/dts/bindings/behaviors/zero_param.yaml +++ b/app/dts/bindings/behaviors/zero_param.yaml @@ -1,6 +1,8 @@ # Copyright (c) 2020 The ZMK Contributors # SPDX-License-Identifier: MIT +include: behavior-metadata.yaml + properties: label: type: string diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index 3936da5e4be..3dd6e0623f7 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -23,6 +23,39 @@ * (Internal use only.) */ +struct behavior_parameter_value_metadata { + char *display_name; + + union { + uint32_t value; + struct { + int32_t min; + int32_t max; + } range; + }; + + enum { + BEHAVIOR_PARAMETER_VALUE_TYPE_NIL = 0, + BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE = 1, + BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE = 2, + BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE = 3, + BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX = 4, + } type; +}; + +struct behavior_parameter_metadata_set { + size_t param1_values_len; + const struct behavior_parameter_value_metadata *param1_values; + + size_t param2_values_len; + const struct behavior_parameter_value_metadata *param2_values; +}; + +struct behavior_parameter_metadata { + size_t sets_len; + const struct behavior_parameter_metadata_set *sets; +}; + enum behavior_sensor_binding_process_mode { BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER, BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD, @@ -37,6 +70,10 @@ typedef int (*behavior_sensor_keymap_binding_accept_data_callback_t)( struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event, const struct zmk_sensor_config *sensor_config, size_t channel_data_size, const struct zmk_sensor_channel_data channel_data[channel_data_size]); +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +typedef int (*behavior_get_parameter_metadata_t)( + const struct device *behavior, struct behavior_parameter_metadata *param_metadata); +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) enum behavior_locality { BEHAVIOR_LOCALITY_CENTRAL, @@ -51,23 +88,54 @@ __subsystem struct behavior_driver_api { behavior_keymap_binding_callback_t binding_released; behavior_sensor_keymap_binding_accept_data_callback_t sensor_binding_accept_data; behavior_sensor_keymap_binding_process_callback_t sensor_binding_process; +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + behavior_get_parameter_metadata_t get_parameter_metadata; + const struct behavior_parameter_metadata *parameter_metadata; +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; /** * @endcond */ +struct zmk_behavior_metadata { +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + const char *display_name; +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +}; + struct zmk_behavior_ref { const struct device *device; + const struct zmk_behavior_metadata metadata; }; +#define ZMK_BEHAVIOR_REF_DT_NAME(node_id) _CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id)) + +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +#define ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id) \ + { .display_name = DT_PROP_OR(node_id, display_name, DEVICE_DT_NAME(node_id)), } + +#else + +#define ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id) \ + {} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +#define ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev) \ + { .device = _dev, .metadata = ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id), } + +#define ZMK_BEHAVIOR_REF_DEFINE(name, node_id, _dev) \ + static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, name) = \ + ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev) + +#define ZMK_BEHAVIOR_REF_DT_DEFINE(node_id) \ + ZMK_BEHAVIOR_REF_DEFINE(ZMK_BEHAVIOR_REF_DT_NAME(node_id), node_id, DEVICE_DT_GET(node_id)) + /** * Registers @p node_id as a behavior. */ -#define BEHAVIOR_DEFINE(node_id) \ - static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, \ - _CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id))) = { \ - .device = DEVICE_DT_GET(node_id), \ - } +#define BEHAVIOR_DEFINE(node_id) ZMK_BEHAVIOR_REF_DT_DEFINE(node_id) /** * @brief Like DEVICE_DT_DEFINE(), but also registers the device as a behavior. @@ -89,6 +157,52 @@ struct zmk_behavior_ref { DEVICE_DT_INST_DEFINE(inst, __VA_ARGS__); \ BEHAVIOR_DEFINE(DT_DRV_INST(inst)) +/** + * @brief Validate a given behavior binding is valid, including parameter validation + * if the metadata feature is enablued. + * + * @param binding The behavior binding to validate. + * + * @retval 0 if the passed in binding is valid. + * @retval -ENODEV if the binding references a non-existant behavior. + * @retval -EINVAL if parameters are not valid for the behavior metadata. + */ +int zmk_behavior_validate_binding(const struct zmk_behavior_binding *binding); + +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +int zmk_behavior_get_empty_param_metadata(const struct device *dev, + struct behavior_parameter_metadata *metadata); + +/** + * @brief Validate a given behavior parameters match the behavior metadata. + * + * @param metadata The behavior metadata to validate against + * @param param1 The first parameter value + * @param param2 The second parameter value + * + * @retval 0 if the passed in parameters are valid. + * @retval -ENODEV if metadata is NULL. + * @retval -EINVAL if parameters are not valid for the metadata. + */ +int zmk_behavior_check_params_match_metadata(const struct behavior_parameter_metadata *metadata, + uint32_t param1, uint32_t param2); +/** + * @brief Validate a given behavior parameter matches the behavior metadata parameter values. + * + * @param values The values to validate against + * @param values_len How many values to check + * @param param The value to check. + * + * @retval 0 if the passed in parameter is valid. + * @retval -ENODEV if values is NULL. + * @retval -EINVAL if parameter is not valid for the value metadata. + */ +int zmk_behavior_validate_param_values(const struct behavior_parameter_value_metadata *values, + size_t values_len, uint32_t param); + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + /** * Syscall wrapper for zmk_behavior_get_binding(). * @@ -120,6 +234,40 @@ static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent return api->binding_convert_central_state_dependent_params(binding, event); } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +/** + * @brief Determine where the behavior should be run + * @param behavior Pointer to the device structure for the driver instance. + * + * @retval Zero if successful. + * @retval Negative errno code if failure. + */ +__syscall int behavior_get_parameter_metadata(const struct device *behavior, + struct behavior_parameter_metadata *param_metadata); + +static inline int +z_impl_behavior_get_parameter_metadata(const struct device *behavior, + struct behavior_parameter_metadata *param_metadata) { + if (behavior == NULL || param_metadata == NULL) { + return -EINVAL; + } + + const struct behavior_driver_api *api = (const struct behavior_driver_api *)behavior->api; + + if (api->get_parameter_metadata) { + return api->get_parameter_metadata(behavior, param_metadata); + } else if (api->parameter_metadata) { + *param_metadata = *api->parameter_metadata; + } else { + return -ENODEV; + } + + return 0; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + /** * @brief Determine where the behavior should be run * @param behavior Pointer to the device structure for the driver instance. diff --git a/app/include/zmk/behavior.h b/app/include/zmk/behavior.h index ab95fd8e728..016fa3bc063 100644 --- a/app/include/zmk/behavior.h +++ b/app/include/zmk/behavior.h @@ -12,7 +12,7 @@ #define ZMK_BEHAVIOR_TRANSPARENT 1 struct zmk_behavior_binding { - char *behavior_dev; + const char *behavior_dev; uint32_t param1; uint32_t param2; }; diff --git a/app/snippets/zmk-usb-logging/snippet.yml b/app/snippets/zmk-usb-logging/snippet.yml new file mode 100644 index 00000000000..8f218085008 --- /dev/null +++ b/app/snippets/zmk-usb-logging/snippet.yml @@ -0,0 +1,4 @@ +name: zmk-usb-logging +append: + EXTRA_CONF_FILE: zmk-usb-logging.conf + EXTRA_DTC_OVERLAY_FILE: zmk-usb-logging.overlay diff --git a/app/snippets/zmk-usb-logging/zmk-usb-logging.conf b/app/snippets/zmk-usb-logging/zmk-usb-logging.conf new file mode 100644 index 00000000000..57893df5f9d --- /dev/null +++ b/app/snippets/zmk-usb-logging/zmk-usb-logging.conf @@ -0,0 +1,2 @@ +CONFIG_ZMK_USB_LOGGING=y + diff --git a/app/snippets/zmk-usb-logging/zmk-usb-logging.overlay b/app/snippets/zmk-usb-logging/zmk-usb-logging.overlay new file mode 100644 index 00000000000..5ceda583f49 --- /dev/null +++ b/app/snippets/zmk-usb-logging/zmk-usb-logging.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + chosen { + zephyr,console = &snippet_zmk_usb_logging_uart; + zephyr,shell-uart = &snippet_zmk_usb_logging_uart; + }; +}; + +&zephyr_udc0 { + snippet_zmk_usb_logging_uart: snippet_zmk_usb_logging_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/app/src/behavior.c b/app/src/behavior.c index fa2005ff1af..7777155f404 100644 --- a/app/src/behavior.c +++ b/app/src/behavior.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -39,6 +41,150 @@ const struct device *z_impl_behavior_get_binding(const char *name) { return NULL; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +int zmk_behavior_get_empty_param_metadata(const struct device *dev, + struct behavior_parameter_metadata *metadata) { + metadata->sets_len = 0; + return 0; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +static int validate_hid_usage(uint16_t usage_page, uint16_t usage_id) { + LOG_DBG("Validate usage %d in page %d", usage_id, usage_page); + switch (usage_page) { + case HID_USAGE_KEY: + if (usage_id == 0 || (usage_id > ZMK_HID_KEYBOARD_NKRO_MAX_USAGE && + usage_id < LEFT_CONTROL && usage_id > RIGHT_GUI)) { + return -EINVAL; + } + break; + case HID_USAGE_CONSUMER: + if (usage_id > + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC), (0xFF), (0xFFF))) { + return -EINVAL; + } + break; + default: + LOG_WRN("Unsupported HID usage page %d", usage_page); + return -EINVAL; + } + + return 0; +} + +#define PARAM_MATCHES 0 + +static int check_param_matches_value(const struct behavior_parameter_value_metadata *value_meta, + uint32_t param) { + switch (value_meta->type) { + case BEHAVIOR_PARAMETER_VALUE_TYPE_NIL: + if (param == 0) { + return PARAM_MATCHES; + } + break; + case BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE: + if (validate_hid_usage(ZMK_HID_USAGE_PAGE(param), ZMK_HID_USAGE_ID(param)) >= 0) { + return PARAM_MATCHES; + } + + break; + case BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX: + if (param >= 0 && param < ZMK_KEYMAP_LEN) { + return PARAM_MATCHES; + } + break; + /* TODO: Restore with HSV -> RGB refactor + case BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV: + // TODO: No real way to validate? Maybe max brightness? + break; + */ + case BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE: + if (param == value_meta->value) { + return PARAM_MATCHES; + } + break; + case BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE: + if (param >= value_meta->range.min && param <= value_meta->range.max) { + return PARAM_MATCHES; + } + break; + default: + LOG_WRN("Unknown type %d", value_meta->type); + break; + } + + return -ENOTSUP; +} + +int zmk_behavior_validate_param_values(const struct behavior_parameter_value_metadata *values, + size_t values_len, uint32_t param) { + if (values_len == 0) { + return -ENODEV; + } + + for (int v = 0; v < values_len; v++) { + int ret = check_param_matches_value(&values[v], param); + if (ret >= 0) { + return ret; + } + } + + return -EINVAL; +} + +int zmk_behavior_check_params_match_metadata(const struct behavior_parameter_metadata *metadata, + uint32_t param1, uint32_t param2) { + if (!metadata || metadata->sets_len == 0) { + if (!metadata) { + LOG_ERR("No metadata to check against"); + } + + return (param1 == 0 && param2 == 0) ? 0 : -ENODEV; + } + + for (int s = 0; s < metadata->sets_len; s++) { + const struct behavior_parameter_metadata_set *set = &metadata->sets[s]; + int param1_ret = + zmk_behavior_validate_param_values(set->param1_values, set->param1_values_len, param1); + int param2_ret = + zmk_behavior_validate_param_values(set->param2_values, set->param2_values_len, param2); + + if ((param1_ret >= 0 || (param1_ret == -ENODEV && param1 == 0)) && + (param2_ret >= 0 || (param2_ret == -ENODEV && param2 == 0))) { + return 0; + } + } + + return -EINVAL; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +int zmk_behavior_validate_binding(const struct zmk_behavior_binding *binding) { +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + const struct device *behavior = zmk_behavior_get_binding(binding->behavior_dev); + + if (!behavior) { + return -ENODEV; + } + + struct behavior_parameter_metadata metadata; + int ret = behavior_get_parameter_metadata(behavior, &metadata); + + if (ret < 0) { + LOG_WRN("Failed getting metadata for %s: %d", binding->behavior_dev, ret); + return ret; + } + + return zmk_behavior_check_params_match_metadata(&metadata, binding->param1, binding->param2); +#else + return 0; +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +} + #if IS_ENABLED(CONFIG_LOG) static int check_behavior_names(void) { // Behavior names must be unique, but we don't have a good way to enforce this diff --git a/app/src/behaviors/behavior_backlight.c b/app/src/behaviors/behavior_backlight.c index 3f836b73d76..d67ce2e7a8a 100644 --- a/app/src/behaviors/behavior_backlight.c +++ b/app/src/behaviors/behavior_backlight.c @@ -18,6 +18,82 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata no_arg_values[] = { + { + .display_name = "Toggle On/Off", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_TOG_CMD, + }, + { + .display_name = "Turn On", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_ON_CMD, + }, + { + .display_name = "Turn OFF", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_OFF_CMD, + }, + { + .display_name = "Increase Brightness", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_INC_CMD, + }, + { + .display_name = "Decrease Brightness", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_DEC_CMD, + }, + { + .display_name = "Cycle Brightness", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_CYCLE_CMD, + }, +}; + +static const struct behavior_parameter_value_metadata one_arg_p1_values[] = { + { + .display_name = "Set Brightness", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BL_SET_CMD, + }, +}; + +static const struct behavior_parameter_value_metadata one_arg_p2_values[] = { + { + .display_name = "Brightness", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE, + .range = + { + .min = 0, + .max = 255, + }, + }, +}; + +static const struct behavior_parameter_metadata_set no_args_set = { + .param1_values = no_arg_values, + .param1_values_len = ARRAY_SIZE(no_arg_values), +}; + +static const struct behavior_parameter_metadata_set one_args_set = { + .param1_values = one_arg_p1_values, + .param1_values_len = ARRAY_SIZE(one_arg_p1_values), + .param2_values = one_arg_p2_values, + .param2_values_len = ARRAY_SIZE(one_arg_p2_values), +}; + +static const struct behavior_parameter_metadata_set sets[] = {no_args_set, one_args_set}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(sets), + .sets = sets, +}; + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static int behavior_backlight_init(const struct device *dev) { return 0; } static int @@ -89,6 +165,9 @@ static const struct behavior_driver_api behavior_backlight_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, .locality = BEHAVIOR_LOCALITY_GLOBAL, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif }; BEHAVIOR_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/behaviors/behavior_bt.c b/app/src/behaviors/behavior_bt.c index 03bb7d8c898..f439e49b1cf 100644 --- a/app/src/behaviors/behavior_bt.c +++ b/app/src/behaviors/behavior_bt.c @@ -20,6 +20,74 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata no_arg_values[] = { + { + .display_name = "Next Profile", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_NXT_CMD, + }, + { + .display_name = "Previous Profile", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_PRV_CMD, + }, + { + .display_name = "Clear All Profiles", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_CLR_ALL_CMD, + }, + { + .display_name = "Clear Selected Profile", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_CLR_CMD, + }, +}; + +static const struct behavior_parameter_metadata_set no_args_set = { + .param1_values = no_arg_values, + .param1_values_len = ARRAY_SIZE(no_arg_values), +}; + +static const struct behavior_parameter_value_metadata prof_index_param1_values[] = { + { + .display_name = "Select Profile", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_SEL_CMD, + }, + { + .display_name = "Disconnect Profile", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = BT_DISC_CMD, + }, +}; + +static const struct behavior_parameter_value_metadata prof_index_param2_values[] = { + { + .display_name = "Profile", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE, + .range = {.min = 0, .max = ZMK_BLE_PROFILE_COUNT}, + }, +}; + +static const struct behavior_parameter_metadata_set profile_index_metadata_set = { + .param1_values = prof_index_param1_values, + .param1_values_len = ARRAY_SIZE(prof_index_param1_values), + .param2_values = prof_index_param2_values, + .param2_values_len = ARRAY_SIZE(prof_index_param2_values), +}; + +static const struct behavior_parameter_metadata_set metadata_sets[] = {no_args_set, + profile_index_metadata_set}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(metadata_sets), + .sets = metadata_sets, +}; + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { switch (binding->param1) { @@ -54,6 +122,9 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_bt_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; BEHAVIOR_DT_INST_DEFINE(0, behavior_bt_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/behaviors/behavior_caps_word.c b/app/src/behaviors/behavior_caps_word.c index d9b3f24ec7d..bf74a4b3dd3 100644 --- a/app/src/behaviors/behavior_caps_word.c +++ b/app/src/behaviors/behavior_caps_word.c @@ -75,6 +75,9 @@ static int on_caps_word_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_caps_word_driver_api = { .binding_pressed = on_caps_word_binding_pressed, .binding_released = on_caps_word_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh); diff --git a/app/src/behaviors/behavior_hold_tap.c b/app/src/behaviors/behavior_hold_tap.c index 57263d1c85e..1c050c44fe5 100644 --- a/app/src/behaviors/behavior_hold_tap.c +++ b/app/src/behaviors/behavior_hold_tap.c @@ -68,6 +68,12 @@ struct behavior_hold_tap_config { int32_t hold_trigger_key_positions[]; }; +struct behavior_hold_tap_data { +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + struct behavior_parameter_metadata_set set; +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +}; + // this data is specific for each hold-tap struct active_hold_tap { int32_t position; @@ -652,9 +658,52 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +static int hold_tap_parameter_metadata(const struct device *hold_tap, + struct behavior_parameter_metadata *param_metadata) { + const struct behavior_hold_tap_config *cfg = hold_tap->config; + struct behavior_hold_tap_data *data = hold_tap->data; + int err; + struct behavior_parameter_metadata child_meta; + + err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->hold_behavior_dev), + &child_meta); + if (err < 0) { + LOG_WRN("Failed to get the hold behavior parameter: %d", err); + return err; + } + + if (child_meta.sets_len > 0) { + data->set.param1_values = child_meta.sets[0].param1_values; + data->set.param1_values_len = child_meta.sets[0].param1_values_len; + } + + err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->tap_behavior_dev), + &child_meta); + if (err < 0) { + LOG_WRN("Failed to get the tap behavior parameter: %d", err); + return err; + } + + if (child_meta.sets_len > 0) { + data->set.param2_values = child_meta.sets[0].param1_values; + data->set.param2_values_len = child_meta.sets[0].param1_values_len; + } + + param_metadata->sets = &data->set; + param_metadata->sets_len = 1; + + return 0; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static const struct behavior_driver_api behavior_hold_tap_driver_api = { .binding_pressed = on_hold_tap_binding_pressed, .binding_released = on_hold_tap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = hold_tap_parameter_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static int position_state_changed_listener(const zmk_event_t *eh) { @@ -791,7 +840,7 @@ static int behavior_hold_tap_init(const struct device *dev) { } #define KP_INST(n) \ - static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \ + static const struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \ .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ .hold_behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \ .tap_behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \ @@ -807,9 +856,10 @@ static int behavior_hold_tap_init(const struct device *dev) { .hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \ .hold_trigger_key_positions_len = DT_INST_PROP_LEN(n, hold_trigger_key_positions), \ }; \ - BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \ - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_hold_tap_driver_api); + static struct behavior_hold_tap_data behavior_hold_tap_data_##n = {}; \ + BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, &behavior_hold_tap_data_##n, \ + &behavior_hold_tap_config_##n, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_hold_tap_driver_api); DT_INST_FOREACH_STATUS_OKAY(KP_INST) diff --git a/app/src/behaviors/behavior_key_press.c b/app/src/behaviors/behavior_key_press.c index 566cfcfba77..b090401ec50 100644 --- a/app/src/behaviors/behavior_key_press.c +++ b/app/src/behaviors/behavior_key_press.c @@ -16,6 +16,27 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata param_values[] = { + { + .display_name = "Key", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE, + }, +}; + +static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ + .param1_values = param_values, + .param1_values_len = ARRAY_SIZE(param_values), +}}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(param_metadata_set), + .sets = param_metadata_set, +}; + +#endif + static int behavior_key_press_init(const struct device *dev) { return 0; }; static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, @@ -31,7 +52,12 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, } static const struct behavior_driver_api behavior_key_press_driver_api = { - .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +}; #define KP_INST(n) \ BEHAVIOR_DT_INST_DEFINE(n, behavior_key_press_init, NULL, NULL, NULL, POST_KERNEL, \ diff --git a/app/src/behaviors/behavior_key_repeat.c b/app/src/behaviors/behavior_key_repeat.c index c93fa722734..f2cd569f635 100644 --- a/app/src/behaviors/behavior_key_repeat.c +++ b/app/src/behaviors/behavior_key_repeat.c @@ -19,6 +19,27 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata param_values[] = { + { + .display_name = "Key", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE, + }, +}; + +static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ + .param1_values = param_values, + .param1_values_len = ARRAY_SIZE(param_values), +}}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(param_metadata_set), + .sets = param_metadata_set, +}; + +#endif + struct behavior_key_repeat_config { uint8_t index; uint8_t usage_pages_count; @@ -67,6 +88,9 @@ static int on_key_repeat_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_key_repeat_driver_api = { .binding_pressed = on_key_repeat_binding_pressed, .binding_released = on_key_repeat_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static int key_repeat_keycode_state_changed_listener(const zmk_event_t *eh); diff --git a/app/src/behaviors/behavior_key_toggle.c b/app/src/behaviors/behavior_key_toggle.c index 0dc0f5abfdc..d967af0146b 100644 --- a/app/src/behaviors/behavior_key_toggle.c +++ b/app/src/behaviors/behavior_key_toggle.c @@ -31,9 +31,34 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, return 0; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata param_values[] = { + { + .display_name = "Key", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, + .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HID_USAGE, + }, +}; + +static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ + .param1_values = param_values, + .param1_values_len = ARRAY_SIZE(param_values), +}}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(param_metadata_set), + .sets = param_metadata_set, +}; + +#endif + static const struct behavior_driver_api behavior_key_toggle_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; #define KT_INST(n) \ diff --git a/app/src/behaviors/behavior_macro.c b/app/src/behaviors/behavior_macro.c index acffe3d8857..b535ed8be07 100644 --- a/app/src/behaviors/behavior_macro.c +++ b/app/src/behaviors/behavior_macro.c @@ -34,6 +34,10 @@ struct behavior_macro_trigger_state { struct behavior_macro_state { struct behavior_macro_trigger_state release_state; +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + struct behavior_parameter_metadata_set set; +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + uint32_t press_bindings_count; }; @@ -209,9 +213,100 @@ static int on_macro_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static void assign_values_to_set(enum param_source param_source, + struct behavior_parameter_metadata_set *set, + const struct behavior_parameter_value_metadata *values, + size_t values_len) { + if (param_source == PARAM_SOURCE_MACRO_1ST) { + set->param1_values = values; + set->param1_values_len = values_len; + } else { + set->param2_values = values; + set->param2_values_len = values_len; + } +} + +// This function will dynamically determine the parameter metadata for a particular macro by +// inspecting the macro *bindings* to see what behaviors in that list receive the macro parameters, +// and then using the metadata from those behaviors for the macro itself. +// +// Care need be taken, where a behavior in the list takes two parameters, and the macro passes along +// a value for the *second* parameter, we need to make sure we find the right metadata set for the +// referenced behavior that matches the first parameter. +static int get_macro_parameter_metadata(const struct device *macro, + struct behavior_parameter_metadata *param_metadata) { + const struct behavior_macro_config *cfg = macro->config; + struct behavior_macro_state *data = macro->data; + struct behavior_macro_trigger_state state = {0}; + + for (int i = 0; (i < cfg->count) && (!data->set.param1_values || !data->set.param2_values); + i++) { + if (handle_control_binding(&state, &cfg->bindings[i]) || + (state.param1_source == PARAM_SOURCE_BINDING && + state.param2_source == PARAM_SOURCE_BINDING)) { + continue; + } + + LOG_DBG("checking %d for the given state", i); + + struct behavior_parameter_metadata binding_meta; + int err = behavior_get_parameter_metadata( + zmk_behavior_get_binding(cfg->bindings[i].behavior_dev), &binding_meta); + if (err < 0 || binding_meta.sets_len == 0) { + LOG_WRN("Failed to fetch macro binding parameter details %d", err); + return -ENOTSUP; + } + + // If both macro parameters get passed to this one entry, use + // the metadata for this behavior verbatim. + if (state.param1_source != PARAM_SOURCE_BINDING && + state.param2_source != PARAM_SOURCE_BINDING) { + param_metadata->sets_len = binding_meta.sets_len; + param_metadata->sets = binding_meta.sets; + return 0; + } + + if (state.param1_source != PARAM_SOURCE_BINDING) { + assign_values_to_set(state.param1_source, &data->set, + binding_meta.sets[0].param1_values, + binding_meta.sets[0].param1_values_len); + } + + if (state.param2_source != PARAM_SOURCE_BINDING) { + // For the param2 metadata, we need to find a set that matches fully bound first + // parameter of our macro entry, and use the metadata from that set. + for (int s = 0; s < binding_meta.sets_len; s++) { + if (zmk_behavior_validate_param_values(binding_meta.sets[s].param1_values, + binding_meta.sets[s].param1_values_len, + cfg->bindings[i].param1) >= 0) { + assign_values_to_set(state.param2_source, &data->set, + binding_meta.sets[s].param2_values, + binding_meta.sets[s].param2_values_len); + break; + } + } + } + + state.param1_source = PARAM_SOURCE_BINDING; + state.param2_source = PARAM_SOURCE_BINDING; + } + + param_metadata->sets_len = 1; + param_metadata->sets = &data->set; + + return 0; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static const struct behavior_driver_api behavior_macro_driver_api = { .binding_pressed = on_macro_binding_pressed, .binding_released = on_macro_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = get_macro_parameter_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; #define TRANSFORMED_BEHAVIORS(n) \ diff --git a/app/src/behaviors/behavior_mod_morph.c b/app/src/behaviors/behavior_mod_morph.c index 3a8bf08c198..303f96a7d05 100644 --- a/app/src/behaviors/behavior_mod_morph.c +++ b/app/src/behaviors/behavior_mod_morph.c @@ -75,6 +75,9 @@ static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_mod_morph_driver_api = { .binding_pressed = on_mod_morph_binding_pressed, .binding_released = on_mod_morph_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static int behavior_mod_morph_init(const struct device *dev) { return 0; } diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index 0c86e605b55..e27889df96f 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -15,6 +15,27 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata param_values[] = { + { + .display_name = "Layer", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX, + }, +}; + +static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ + .param1_values = param_values, + .param1_values_len = ARRAY_SIZE(param_values), +}}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(param_metadata_set), + .sets = param_metadata_set, +}; + +#endif + struct behavior_mo_config {}; struct behavior_mo_data {}; @@ -33,7 +54,12 @@ static int mo_keymap_binding_released(struct zmk_behavior_binding *binding, } static const struct behavior_driver_api behavior_mo_driver_api = { - .binding_pressed = mo_keymap_binding_pressed, .binding_released = mo_keymap_binding_released}; + .binding_pressed = mo_keymap_binding_pressed, + .binding_released = mo_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) +}; static const struct behavior_mo_config behavior_mo_config = {}; diff --git a/app/src/behaviors/behavior_none.c b/app/src/behaviors/behavior_none.c index 0137622aceb..b1dc4ad3327 100644 --- a/app/src/behaviors/behavior_none.c +++ b/app/src/behaviors/behavior_none.c @@ -31,6 +31,9 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_none_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; BEHAVIOR_DT_INST_DEFINE(0, behavior_none_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/behaviors/behavior_outputs.c b/app/src/behaviors/behavior_outputs.c index d172c3a11b8..ffa57d16357 100644 --- a/app/src/behaviors/behavior_outputs.c +++ b/app/src/behaviors/behavior_outputs.c @@ -20,6 +20,42 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata std_values[] = { + { + .value = OUT_TOG, + .display_name = "Toggle Outputs", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + }, +#if IS_ENABLED(CONFIG_ZMK_USB) + { + .value = OUT_USB, + .display_name = "USB Output", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + }, +#endif // IS_ENABLED(CONFIG_ZMK_USB) +#if IS_ENABLED(CONFIG_ZMK_BLE) + { + .value = OUT_BLE, + .display_name = "BLE Output", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + }, +#endif // IS_ENABLED(CONFIG_ZMK_BLE) +}; + +static const struct behavior_parameter_metadata_set std_set = { + .param1_values = std_values, + .param1_values_len = ARRAY_SIZE(std_values), +}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = 1, + .sets = &std_set, +}; + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { switch (binding->param1) { @@ -40,6 +76,9 @@ static int behavior_out_init(const struct device *dev) { return 0; } static const struct behavior_driver_api behavior_outputs_driver_api = { .binding_pressed = on_keymap_binding_pressed, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; BEHAVIOR_DT_INST_DEFINE(0, behavior_out_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/behaviors/behavior_reset.c b/app/src/behaviors/behavior_reset.c index c559f17fd6f..554132f4a1e 100644 --- a/app/src/behaviors/behavior_reset.c +++ b/app/src/behaviors/behavior_reset.c @@ -38,6 +38,9 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_reset_driver_api = { .binding_pressed = on_keymap_binding_pressed, .locality = BEHAVIOR_LOCALITY_EVENT_SOURCE, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; #define RST_INST(n) \ diff --git a/app/src/behaviors/behavior_rgb_underglow.c b/app/src/behaviors/behavior_rgb_underglow.c index a16ee591ed4..c37e5217c73 100644 --- a/app/src/behaviors/behavior_rgb_underglow.c +++ b/app/src/behaviors/behavior_rgb_underglow.c @@ -18,6 +18,119 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata no_arg_values[] = { + { + .display_name = "Toggle On/Off", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_TOG_CMD, + }, + { + .display_name = "Turn On", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_ON_CMD, + }, + { + .display_name = "Turn OFF", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_OFF_CMD, + }, + { + .display_name = "Hue Up", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_HUI_CMD, + }, + { + .display_name = "Hue Down", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_HUD_CMD, + }, + { + .display_name = "Saturation Up", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_SAI_CMD, + }, + { + .display_name = "Saturation Down", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_SAD_CMD, + }, + { + .display_name = "Brightness Up", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_BRI_CMD, + }, + { + .display_name = "Brightness Down", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_BRD_CMD, + }, + { + .display_name = "Speed Up", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_SPI_CMD, + }, + { + .display_name = "Speed Down", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_SPD_CMD, + }, + { + .display_name = "Next Effect", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_EFF_CMD, + }, + { + .display_name = "Previous Effect", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_EFR_CMD, + }, +}; + +static const struct behavior_parameter_metadata_set no_args_set = { + .param1_values = no_arg_values, + .param1_values_len = ARRAY_SIZE(no_arg_values), +}; + +/* +static const struct behavior_parameter_value_metadata hsv_p1_value_metadata_values[] = { + { + .display_name = "Set Color", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE, + .value = RGB_COLOR_HSB_CMD, + }, +}; + +static const struct behavior_parameter_value_metadata hsv_p2_value_metadata_values[] = { + { + .display_name = "Color", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, + .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV, + }, +}; + +static const struct behavior_parameter_metadata_set hsv_value_metadata_set = { + .param1_values = hsv_p1_value_metadata_values, + .param1_values_len = ARRAY_SIZE(hsv_p1_value_metadata_values), + .param_values = hsv_p2_value_metadata_values, + .param_values_len = ARRAY_SIZE(hsv_p2_value_metadata_values), +}; + +*/ + +static const struct behavior_parameter_metadata_set sets[] = { + no_args_set, + // hsv_value_metadata_set, +}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(sets), + .sets = sets, +}; + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static int behavior_rgb_underglow_init(const struct device *dev) { return 0; } static int @@ -147,6 +260,9 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, .locality = BEHAVIOR_LOCALITY_GLOBAL, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif }; BEHAVIOR_DT_INST_DEFINE(0, behavior_rgb_underglow_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/behaviors/behavior_soft_off.c b/app/src/behaviors/behavior_soft_off.c index 461ce933cf7..fcffd09ae5e 100644 --- a/app/src/behaviors/behavior_soft_off.c +++ b/app/src/behaviors/behavior_soft_off.c @@ -74,6 +74,9 @@ static const struct behavior_driver_api behavior_soft_off_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, .locality = BEHAVIOR_LOCALITY_GLOBAL, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; #define BSO_INST(n) \ diff --git a/app/src/behaviors/behavior_sticky_key.c b/app/src/behaviors/behavior_sticky_key.c index b0e9f3ed0c0..d1299c78d7f 100644 --- a/app/src/behaviors/behavior_sticky_key.c +++ b/app/src/behaviors/behavior_sticky_key.c @@ -188,9 +188,41 @@ static int on_sticky_key_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static int sticky_key_parameter_domains(const struct device *sk, + struct behavior_parameter_metadata *param_metadata) { + const struct behavior_sticky_key_config *cfg = sk->config; + + struct behavior_parameter_metadata child_metadata; + + int err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->behavior.behavior_dev), + &child_metadata); + if (err < 0) { + LOG_WRN("Failed to get the sticky key bound behavior parameter: %d", err); + } + + for (int s = 0; s < child_metadata.sets_len; s++) { + const struct behavior_parameter_metadata_set *set = &child_metadata.sets[s]; + + if (set->param2_values_len > 0) { + return -ENOTSUP; + } + } + + *param_metadata = child_metadata; + + return 0; +} + +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + static const struct behavior_driver_api behavior_sticky_key_driver_api = { .binding_pressed = on_sticky_key_binding_pressed, .binding_released = on_sticky_key_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = sticky_key_parameter_domains, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh); @@ -337,7 +369,7 @@ struct behavior_sticky_key_data {}; static struct behavior_sticky_key_data behavior_sticky_key_data; #define KP_INST(n) \ - static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \ + static const struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \ .behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \ .release_after_ms = DT_INST_PROP(n, release_after_ms), \ .quick_release = DT_INST_PROP(n, quick_release), \ diff --git a/app/src/behaviors/behavior_tap_dance.c b/app/src/behaviors/behavior_tap_dance.c index 4f6fa1a134e..ce57b70fc4b 100644 --- a/app/src/behaviors/behavior_tap_dance.c +++ b/app/src/behaviors/behavior_tap_dance.c @@ -189,6 +189,9 @@ void behavior_tap_dance_timer_handler(struct k_work *item) { static const struct behavior_driver_api behavior_tap_dance_driver_api = { .binding_pressed = on_tap_dance_binding_pressed, .binding_released = on_tap_dance_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static int tap_dance_position_state_changed_listener(const zmk_event_t *eh); diff --git a/app/src/behaviors/behavior_to_layer.c b/app/src/behaviors/behavior_to_layer.c index 1c87a925905..d260087efce 100644 --- a/app/src/behaviors/behavior_to_layer.c +++ b/app/src/behaviors/behavior_to_layer.c @@ -32,9 +32,34 @@ static int to_keymap_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata param_values[] = { + { + .display_name = "Layer", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, + .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX, + }, +}; + +static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ + .param1_values = param_values, + .param1_values_len = ARRAY_SIZE(param_values), +}}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(param_metadata_set), + .sets = param_metadata_set, +}; + +#endif + static const struct behavior_driver_api behavior_to_driver_api = { .binding_pressed = to_keymap_binding_pressed, .binding_released = to_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; BEHAVIOR_DT_INST_DEFINE(0, behavior_to_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/behaviors/behavior_toggle_layer.c b/app/src/behaviors/behavior_toggle_layer.c index 817462df5aa..df261ed3a34 100644 --- a/app/src/behaviors/behavior_toggle_layer.c +++ b/app/src/behaviors/behavior_toggle_layer.c @@ -34,9 +34,34 @@ static int tog_keymap_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + +static const struct behavior_parameter_value_metadata param_values[] = { + { + .display_name = "Layer", + .type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD, + .standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX, + }, +}; + +static const struct behavior_parameter_metadata_set param_metadata_set[] = {{ + .param1_values = param_values, + .param1_values_len = ARRAY_SIZE(param_values), +}}; + +static const struct behavior_parameter_metadata metadata = { + .sets_len = ARRAY_SIZE(param_metadata_set), + .sets = param_metadata_set, +}; + +#endif + static const struct behavior_driver_api behavior_tog_driver_api = { .binding_pressed = tog_keymap_binding_pressed, .binding_released = tog_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .parameter_metadata = &metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; static const struct behavior_tog_config behavior_tog_config = {}; diff --git a/app/src/behaviors/behavior_transparent.c b/app/src/behaviors/behavior_transparent.c index c7bf802b9fc..32357046724 100644 --- a/app/src/behaviors/behavior_transparent.c +++ b/app/src/behaviors/behavior_transparent.c @@ -31,6 +31,9 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding, static const struct behavior_driver_api behavior_transparent_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released, +#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) + .get_parameter_metadata = zmk_behavior_get_empty_param_metadata, +#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) }; BEHAVIOR_DT_INST_DEFINE(0, behavior_transparent_init, NULL, NULL, NULL, POST_KERNEL, diff --git a/app/src/hid_listener.c b/app/src/hid_listener.c index 2b8470820a1..2d17a395407 100644 --- a/app/src/hid_listener.c +++ b/app/src/hid_listener.c @@ -66,6 +66,17 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed return err; } +#if IS_ENABLED(CONFIG_ZMK_HID_SEPARATE_MOD_RELEASE_REPORT) + + // send report of normal key release early to fix the issue + // of some programs recognizing the implicit_mod release before the actual key release + err = zmk_endpoints_send_report(ev->usage_page); + if (err < 0) { + LOG_ERR("Failed to send key report for the released keycode (%d)", err); + } + +#endif // IS_ENABLED(CONFIG_ZMK_HID_SEPARATE_MOD_RELEASE_REPORT) + explicit_mods_changed = zmk_hid_unregister_mods(ev->explicit_modifiers); // There is a minor issue with this code. // If LC(A) is pressed, then LS(B), then LC(A) is released, the shift for B will be released @@ -73,7 +84,7 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed // Solving this would require keeping track of which key's implicit modifiers are currently // active and only releasing modifiers at that time. implicit_mods_changed = zmk_hid_implicit_modifiers_release(); - ; + if (ev->usage_page != HID_USAGE_KEY && (explicit_mods_changed > 0 || implicit_mods_changed > 0)) { err = zmk_endpoints_send_report(HID_USAGE_KEY); diff --git a/app/src/usb_hid.c b/app/src/usb_hid.c index cd3ef920391..9db10952c95 100644 --- a/app/src/usb_hid.c +++ b/app/src/usb_hid.c @@ -195,4 +195,4 @@ static int zmk_usb_hid_init(void) { return 0; } -SYS_INIT(zmk_usb_hid_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); +SYS_INIT(zmk_usb_hid_init, APPLICATION, CONFIG_ZMK_USB_HID_INIT_PRIORITY); diff --git a/docs/blog/2020-08-12-zmk-sotf-1.md b/docs/blog/2020-08-12-zmk-sotf-1.md index afa03405a70..89cfffabe65 100644 --- a/docs/blog/2020-08-12-zmk-sotf-1.md +++ b/docs/blog/2020-08-12-zmk-sotf-1.md @@ -19,7 +19,7 @@ There's been lots of various activity in ZMK land! - Tons of [documentation](/docs) work. - Refactoring ([#73](https://github.com/zmkfirmware/zmk/pull/73), [#74](https://github.com/zmkfirmware/zmk/pull/74)) of [keymaps](/docs/features/keymaps) to make them simpler for users. - Mod-Tap Behavior (docs coming!) is much improved ([#69](https://github.com/zmkfirmware/zmk/pull/69)) and usable now. -- An initial [`setup.sh`](http://localhost:3000/docs/user-setup#user-config-setup-script) script was created, allowing users to quickly bootstrap a "user config" setup and push it to GitHub, where GitHub Actions will build the firmware for you. +- An initial [`setup.sh`](/docs/user-setup#user-config-setup-script) script was created, allowing users to quickly bootstrap a "user config" setup and push it to GitHub, where GitHub Actions will build the firmware for you. - Corne shield ([#80](https://github.com/zmkfirmware/zmk/pull/80)) shield definition was added. - Initial [encoder](/docs/features/encoders) support ([#61](https://github.com/zmkfirmware/zmk/pull/61)) was added. diff --git a/docs/blog/2021-07-17-zephyr-2-5.md b/docs/blog/2021-07-17-zephyr-2-5.md index 153027bb4cd..789a644caee 100644 --- a/docs/blog/2021-07-17-zephyr-2-5.md +++ b/docs/blog/2021-07-17-zephyr-2-5.md @@ -61,7 +61,7 @@ Once the container has rebuilt, VS Code will be running the 2.5 Docker image. The following steps will get you building ZMK locally against Zephyr 2.5: -- Run the updated [toolchain installation](/docs/development/setup#toolchain-installation) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) +- Run the updated [toolchain installation](/docs/development/setup) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) - pull the latest ZMK `main` with `git pull` for your ZMK checkout - run `west update` to pull the updated Zephyr version and its dependencies diff --git a/docs/blog/2022-04-02-zephyr-3-0.md b/docs/blog/2022-04-02-zephyr-3-0.md index 3b16b87ea78..92e8b33bc1c 100644 --- a/docs/blog/2022-04-02-zephyr-3-0.md +++ b/docs/blog/2022-04-02-zephyr-3-0.md @@ -62,7 +62,7 @@ Once the container has rebuilt, VS Code will be running the 3.0 Docker image. The following steps will get you building ZMK locally against Zephyr 3.0: -- Run the updated [toolchain installation](/docs/development/setup#toolchain-installation) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) +- Run the updated [toolchain installation](/docs/development/setup) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) - pull the latest ZMK `main` with `git pull` for your ZMK checkout - run `west update` to pull the updated Zephyr version and its dependencies diff --git a/docs/blog/2022-04-10-zmk-sotf-5.md b/docs/blog/2022-04-10-zmk-sotf-5.md index 43cb4e67209..1a0ea270c8d 100644 --- a/docs/blog/2022-04-10-zmk-sotf-5.md +++ b/docs/blog/2022-04-10-zmk-sotf-5.md @@ -132,7 +132,7 @@ Another persistent bug that Apple users experienced was related to crashes and p The long awaited locality enhancement was finally merged by [petejohanson] in [#547](https://github.com/zmkfirmware/zmk/pull/547), allowing more fine grained control of where certain behaviors are invoked. Some key improvements thanks to the changes: - [RGB Underglow](/docs/features/underglow) behaviors now run globally, so enabling/disabling RGB, changing the color, animation, etc. applies to both sides of a split properly. -- [Reset](/docs/behaviors/reset#reset)/[Bootloader](/docs/behaviors/reset#bootloader) behaviors now run wherever the key was pressed. For example, adding a `&bootloader` reference to the peripheral side of a split will now put that side of the split into the bootloader when pressed. +- [Reset](/docs/behaviors/reset#reset)/[Bootloader](/docs/behaviors/reset#bootloader-reset) behaviors now run wherever the key was pressed. For example, adding a `&bootloader` reference to the peripheral side of a split will now put that side of the split into the bootloader when pressed. #### Split Connections diff --git a/docs/blog/2023-04-06-zephyr-3-2.md b/docs/blog/2023-04-06-zephyr-3-2.md index 4ba71cacdb2..21058ca9544 100644 --- a/docs/blog/2023-04-06-zephyr-3-2.md +++ b/docs/blog/2023-04-06-zephyr-3-2.md @@ -57,7 +57,7 @@ and then update it as appropriate to build the right shields/boards for your con ### Upgrade a manual script -If you have a custom GitHub Actions workflow you need to maintain for some reason, you can update the workflow to to use the `stable` Docker image tag for the build: +If you have a custom GitHub Actions workflow you need to maintain for some reason, you can update the workflow to use the `stable` Docker image tag for the build: - Open `.github/workflows/build.yml` in your editor/IDE - Change `zmkfirmware/zmk-build-arm:2.5` to `zmkfirmware/zmk-build-arm:stable` wherever it is found @@ -87,7 +87,7 @@ Once the container has rebuilt, VS Code will be running the 3.2 Docker image. The following steps will get you building ZMK locally against Zephyr 3.2: -- Run the updated [toolchain installation](/docs/development/setup#toolchain-installation) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) +- Run the updated [toolchain installation](/docs/development/setup) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) - Install the latest version of `west` by running `pip3 install --user --update west`. - pull the latest ZMK `main` with `git pull` for your ZMK checkout - run `west update` to pull the updated Zephyr version and its dependencies diff --git a/docs/blog/2023-10-05-zmk-sotf-6.md b/docs/blog/2023-10-05-zmk-sotf-6.md index fb8dc131d21..bd818cf67dc 100644 --- a/docs/blog/2023-10-05-zmk-sotf-6.md +++ b/docs/blog/2023-10-05-zmk-sotf-6.md @@ -105,7 +105,7 @@ Note that documentation is still lacking for utilizing more than one peripheral [petejohanson] contributed a fix to release held keys on peripheral disconnect [#1340](https://github.com/zmkfirmware/zmk/pull/1340), which makes scenarios where a split disconnects unexpectedly less painful. -[petejohanson] also improved [the `settings_reset` shield](/docs/troubleshooting#split-keyboard-halves-unable-to-pair) by making it clear bonds more reliably, and allow it to build for all boards in [#1879](https://github.com/zmkfirmware/zmk/pull/1879). +[petejohanson] also improved [the `settings_reset` shield](/docs/troubleshooting/connection-issues#split-keyboard-halves-unable-to-pair) by making it clear bonds more reliably, and allow it to build for all boards in [#1879](https://github.com/zmkfirmware/zmk/pull/1879). [petejohanson] and [xudongzheng] contributed additional split connectivity improvements, via using directed advertising in [#1913](https://github.com/zmkfirmware/zmk/pull/1913) and improving the robustness of central scanning in [#1912](https://github.com/zmkfirmware/zmk/pull/1912). diff --git a/docs/blog/2024-02-09-zephyr-3-5.md b/docs/blog/2024-02-09-zephyr-3-5.md index b3fec6cbda2..738f22daa81 100644 --- a/docs/blog/2024-02-09-zephyr-3-5.md +++ b/docs/blog/2024-02-09-zephyr-3-5.md @@ -70,7 +70,7 @@ Once the container has rebuilt, VS Code will be running the 3.5 Docker image. The following steps will get you building ZMK locally against Zephyr 3.5: -- Run the updated [toolchain installation](/docs/development/setup#toolchain-installation) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) +- Run the updated [toolchain installation](/docs/development/setup) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work) - Install the latest version of `west` by running `pip3 install --user --update west`. - Pull the latest ZMK `main` with `git pull` for your ZMK checkout - Run `west update` to pull the updated Zephyr version and its dependencies diff --git a/docs/docs/behaviors/caps-word.md b/docs/docs/behaviors/caps-word.md index 77c8fd20d2a..c79ebae01a6 100644 --- a/docs/docs/behaviors/caps-word.md +++ b/docs/docs/behaviors/caps-word.md @@ -7,7 +7,7 @@ sidebar_label: Caps Word The caps word behavior behaves similar to a caps lock, but will automatically deactivate when any key not in a continue list is pressed, or if the caps word key is pressed again. For smaller keyboards using [mod-taps](/docs/behaviors/mod-tap), this can help avoid repeated alternating holds when typing words in all caps. -The modifiers are applied only to to the alphabetic (`A` to `Z`) keycodes, to avoid automatically applying them to numeric values, etc. +The modifiers are applied only to the alphabetic (`A` to `Z`) keycodes, to avoid automatically applying them to numeric values, etc. ### Behavior Binding @@ -21,7 +21,7 @@ Example: ### Configuration -#### Continue List +#### Continue list By default, the caps word will remain active when any alphanumeric character or underscore (`UNDERSCORE`), backspace (`BACKSPACE`), or delete (`DELETE`) characters are pressed. Any other non-modifier keycode sent will turn off caps word. If you would like to override this, you can set a new array of keys in the `continue-list` property in your keymap: @@ -37,7 +37,7 @@ By default, the caps word will remain active when any alphanumeric character or }; ``` -#### Applied Modifier(s) +#### Applied modifier(s) In addition, if you would like _multiple_ modifiers, instead of just `MOD_LSFT`, you can override the `mods` property: diff --git a/docs/docs/behaviors/hold-tap.mdx b/docs/docs/behaviors/hold-tap.mdx index 5217cf79e29..20aaf810a8c 100644 --- a/docs/docs/behaviors/hold-tap.mdx +++ b/docs/docs/behaviors/hold-tap.mdx @@ -35,7 +35,7 @@ When the hold-tap key is released and the hold behavior has not been triggered, ![Hold-tap comparison](../assets/hold-tap/comparison.svg) -### Basic usage +### Basic Usage For basic usage, please see the [mod-tap](mod-tap.md) and [layer-tap](layers.md#layer-tap) pages. diff --git a/docs/docs/behaviors/index.mdx b/docs/docs/behaviors/index.mdx index 4a05f5653a0..bdacc209ad7 100644 --- a/docs/docs/behaviors/index.mdx +++ b/docs/docs/behaviors/index.mdx @@ -9,7 +9,7 @@ sidebar_label: Overview Below is a summary of pre-defined behavior bindings and user-definable behaviors available in ZMK, with references to documentation pages describing them. -## Key press behaviors +## Key Press Behaviors | Binding | Behavior | Description | | ------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -21,14 +21,14 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors | `&caps_word` | [Caps Word](caps-word.md) | Behaves similar to caps lock, but automatically deactivates when any key not in a continue list is pressed, or if the caps word key is pressed again | | `&key_repeat` | [Key Repeat](key-repeat.md) | Sends again whatever keycode was last sent | -## Miscellaneous behaviors +## Miscellaneous Behaviors | Binding | Behavior | Description | | -------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `&trans` | [Transparent](misc.md#transparent) | Passes the key press down to the next active layer in the stack for processing | | `&none` | [None](misc.md#none) | Swallows and stops the key press, no keycode will be sent nor will the key press be passed down to the next active layer in the stack | -## Layer navigation behaviors +## Layer Navigation Behaviors | Binding | Behavior | Description | | ------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------- | @@ -38,41 +38,41 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors | `&tog` | [Toggle Layer](layers.md#toggle-layer) | Enables a layer until the layer is manually disabled | | `&sl` | [Sticky Layer](sticky-layer.md) | Activates a layer until another key is pressed, then deactivates it | -## Mouse emulation behaviors +## Mouse Emulation Behaviors | Binding | Behavior | Description | | ------- | ----------------------------------------------------------- | ------------------------------- | | `&mkp` | [Mouse Button Press](mouse-emulation.md#mouse-button-press) | Emulates pressing mouse buttons | -## Reset behaviors +## Reset Behaviors -| Binding | Behavior | Description | -| ------------- | --------------------------------- | ---------------------------------------------------------------------------------------- | -| `&sys_reset` | [Reset](reset.md#reset) | Resets the keyboard and re-runs the firmware flashed to the device | -| `&bootloader` | [Bootloader](reset.md#bootloader) | Resets the keyboard and puts it into bootloader mode, allowing you to flash new firmware | +| Binding | Behavior | Description | +| ------------- | --------------------------------------- | ---------------------------------------------------------------------------------------- | +| `&sys_reset` | [Reset](reset.md#reset) | Resets the keyboard and re-runs the firmware flashed to the device | +| `&bootloader` | [Bootloader](reset.md#bootloader-reset) | Resets the keyboard and puts it into bootloader mode, allowing you to flash new firmware | -## Output selection behaviors +## Output Selection Behaviors | Binding | Behavior | Description | | ------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `&bt` | [Bluetooth](bluetooth.md#bluetooth-behavior) | Completes a bluetooth action given on press, for example switching between devices | | `&out` | [Output Selection](outputs.md#output-selection-behavior) | Allows selecting whether output is sent to the USB or bluetooth connection when both are connected | -## Lighting behaviors +## Lighting Behaviors | Binding | Behavior | Description | | --------- | ---------------------------------------------- | ---------------------------------------------------------------------------- | | `&rgb_ug` | [RGB Underglow](underglow.md#behavior-binding) | Controls the RGB underglow, usually placed underneath the keyboard | | `&bl` | [Backlight](backlight.md#behavior-binding) | Controls the keyboard backlighting, usually placed through or under switches | -## Power management behaviors +## Power Management Behaviors | Binding | Behavior | Description | | ------------ | --------------------------------------------- | --------------------------------------------------------------- | | `&ext_power` | [Power management](power.md#behavior-binding) | Allows enabling or disabling the VCC power output to save power | | `&soft_off` | [Soft off](soft-off.md#behavior-binding) | Turns the keyboard off. | -## User-defined behaviors +## User-Defined Behaviors | Behavior | Description | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | diff --git a/docs/docs/behaviors/key-repeat.md b/docs/docs/behaviors/key-repeat.md index 9772fdb4e0a..2187008417a 100644 --- a/docs/docs/behaviors/key-repeat.md +++ b/docs/docs/behaviors/key-repeat.md @@ -19,7 +19,7 @@ Example: ### Configuration -#### Usage Pages +#### Usage pages By default, the key repeat will only track the last pressed key from the HID "Key" usage page, and ignore events from other usages, e.g. Consumer page. diff --git a/docs/docs/behaviors/layers.md b/docs/docs/behaviors/layers.md index 7cfb4df7e00..511fbe08b7a 100644 --- a/docs/docs/behaviors/layers.md +++ b/docs/docs/behaviors/layers.md @@ -8,7 +8,7 @@ sidebar_label: Layers Often, you may want a certain key position to alter which layers are enabled, change the default layer, etc. Some of those behaviors are still in the works; the ones that are working now are documented here. -## Defines To Refer To Layers +## Defines to Refer to Layers When working with layers, you may have several different key positions with bindings that enable/disable those layers. To make it easier to refer to those layers in your key bindings, and to change which layers are where later, you can @@ -41,7 +41,7 @@ Example: &mo LOWER ``` -## Layer-tap +## Layer-Tap The "layer-tap" behavior enables a layer when a key is held, and outputs a [keypress](key-press.md) when the key is only tapped for a short time. diff --git a/docs/docs/behaviors/macros.md b/docs/docs/behaviors/macros.md index 37ca80ffbb5..50c8945eb80 100644 --- a/docs/docs/behaviors/macros.md +++ b/docs/docs/behaviors/macros.md @@ -223,7 +223,7 @@ Common examples are enabling one or more modifiers when the layer is active, or To achieve this, a combination of a 0ms wait time and splitting the press and release between a `¯o_pause_for_release` is used: -#### Layer + Modifier +#### Layer + modifier ```dts /** @@ -256,7 +256,7 @@ lm: lm { }; ``` -#### Layer + Underglow Color +#### Layer + underglow color To trigger a different underglow when the macro is pressed, and when it is released, we use the macro "press" activation mode whenever triggering the `&rgb_ug` behavior: diff --git a/docs/docs/behaviors/mod-morph.md b/docs/docs/behaviors/mod-morph.md index 7ecb9ab8983..8949ec2c788 100644 --- a/docs/docs/behaviors/mod-morph.md +++ b/docs/docs/behaviors/mod-morph.md @@ -65,7 +65,7 @@ Example: mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; ``` -### Advanced configuration +### Advanced Configuration `keep-mods` diff --git a/docs/docs/behaviors/soft-off.md b/docs/docs/behaviors/soft-off.md index 14b5f36aee7..086b5d75d75 100644 --- a/docs/docs/behaviors/soft-off.md +++ b/docs/docs/behaviors/soft-off.md @@ -23,7 +23,7 @@ Example: ### Configuration -#### Hold Time +#### Hold time By default, the keyboard will be turned off as soon as the key bound to the behavior is released, even if the key is only tapped briefly. If you would prefer that the key need be held a certain amount of time before releasing, you can set the `hold-time-ms` to a non-zero value in your keymap: diff --git a/docs/docs/behaviors/sticky-key.md b/docs/docs/behaviors/sticky-key.md index 8c3962f5450..30345882a66 100644 --- a/docs/docs/behaviors/sticky-key.md +++ b/docs/docs/behaviors/sticky-key.md @@ -81,7 +81,7 @@ This configuration would apply to all sticky keys. This may not be appropriate i }; ``` -### Advanced usage +### Advanced Usage Sticky keys can be combined; if you tap `&sk LCTRL` and then `&sk LSHIFT` and then `&kp A`, the output will be ctrl+shift+a. diff --git a/docs/docs/behaviors/sticky-layer.md b/docs/docs/behaviors/sticky-layer.md index 41c2ccf55d7..6a231f08995 100644 --- a/docs/docs/behaviors/sticky-layer.md +++ b/docs/docs/behaviors/sticky-layer.md @@ -36,7 +36,7 @@ You can configure a different `release-after-ms` in your keymap: }; ``` -### Advanced usage +### Advanced Usage Sticky layers behave slightly different from sticky keys. They are configured to `quick-release`. This means that the layer is released immediately when another key is pressed. "Normal" sticky keys are not `quick-release`; they are released when the next key is released. This makes it possible to combine sticky layers and sticky keys as such: tap `&sl 1`, tap `&sk LSHIFT` on layer 1, tap `&kp A` on base layer to output shift+A. diff --git a/docs/docs/config/battery.md b/docs/docs/config/battery.md index c95e78841e8..4596395b806 100644 --- a/docs/docs/config/battery.md +++ b/docs/docs/config/battery.md @@ -28,7 +28,7 @@ On macOS the BLE battery reporting packets can cause the computer to wakeup from ::: -### Peripheral battery monitoring +### Peripheral Battery Monitoring You can [configure ZMK to allow support for peripheral battery monitoring over BLE](system.md#split-keyboards) (e.g. when having a split keyboard with two independent and wirelessly connected sides). If you want to report the battery levels of both sides of a split keyboard, you should have both `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY` and `CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING` set to `y`. diff --git a/docs/docs/config/encoders.md b/docs/docs/config/encoders.md index b242f49b5ba..c8966846efb 100644 --- a/docs/docs/config/encoders.md +++ b/docs/docs/config/encoders.md @@ -29,7 +29,7 @@ If `CONFIG_EC11` is enabled, exactly one of the following options must be set to ### Devicetree -#### Keymap Sensor Config +#### Keymap sensor config For shields/boards that export a `sensors` node configuration label, both global and per-sensor settings can be set by overriding the properties there. @@ -69,7 +69,7 @@ Definition file: [zmk/app/drivers/zephyr/dts/bindings/zmk,keymap-sensors.yaml](h | ----------------------- | ---- | --------------------------------------------------------------- | ------- | | `triggers-per-rotation` | int | Number of times to trigger the bound behavior per full rotation | | -#### EC11 Nodes +#### EC11 nodes Applies to: `compatible = "alps,ec11"` diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 159a5366135..b6f9e2ee54d 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -31,10 +31,11 @@ Making changes to any of the settings in this section modifies the HID report de ::: -| Config | Type | Description | Default | -| ------------------------------------- | ---- | -------------------------------------------------------------- | ------- | -| `CONFIG_ZMK_HID_INDICATORS` | bool | Enable receipt of HID/LED indicator state from connected hosts | n | -| `CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE` | int | Number of consumer keys simultaneously reportable | 6 | +| Config | Type | Description | Default | +| -------------------------------------------- | ---- | ---------------------------------------------------------------- | ------- | +| `CONFIG_ZMK_HID_INDICATORS` | bool | Enable receipt of HID/LED indicator state from connected hosts | n | +| `CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE` | int | Number of consumer keys simultaneously reportable | 6 | +| `CONFIG_ZMK_HID_SEPARATE_MOD_RELEASE_REPORT` | bool | Send modifier release event **after** non-modifier release event | n | Exactly zero or one of the following options may be set to `y`. The first is used if none are set. diff --git a/docs/docs/customization.md b/docs/docs/customization.md index 46427b9e2f9..87d78a22b1c 100644 --- a/docs/docs/customization.md +++ b/docs/docs/customization.md @@ -40,7 +40,7 @@ If you need to, a review of [Learn The Basics Of Git In Under 10 Minutes](https: ::: :::note -It is also possible to build firmware locally on your computer by following the [toolchain setup](development/setup.mdx) and +It is also possible to build firmware locally on your computer by following the [toolchain setup](development/setup/index.md) and [building instructions](development/build-flash.mdx), which includes pointers to [building using your `zmk-config` folder](development/build-flash.mdx#building-from-zmk-config-folder). ::: @@ -50,7 +50,7 @@ It is also possible to build firmware locally on your computer by following the For normal keyboards, follow the same flashing instructions as before to flash your updated firmware. For split keyboards, only the central (left) side will need to be reflashed if you are just updating your keymap. -More troubleshooting information for split keyboards can be found [here](troubleshooting.md#split-keyboard-halves-unable-to-pair). +More troubleshooting information for split keyboards can be found [here](troubleshooting/connection-issues.mdx#split-keyboard-halves-unable-to-pair). ## Building Additional Keyboards diff --git a/docs/docs/development/build-flash.mdx b/docs/docs/development/build-flash.mdx index 2cbcf5b82ef..20e9e20a34c 100644 --- a/docs/docs/development/build-flash.mdx +++ b/docs/docs/development/build-flash.mdx @@ -77,7 +77,7 @@ This produces `left` and `right` subfolders under the `build` directory and two Build times can be significantly reduced after the initial build by omitting all build arguments except the build directory, e.g. `west build -d build/left`. The additional options and intermediate build outputs from your initial build are cached and reused for unchanged files. ::: -### Building with external modules +### Building With External Modules ZMK supports loading additional boards, shields, code, etc. from [external Zephyr modules](https://docs.zephyrproject.org/3.5.0/develop/modules.html), facilitating out-of-tree management and versioning independent of the ZMK repository. To build with any additional modules, use the `ZMK_EXTRA_MODULES` define added to your `west build` command. diff --git a/docs/docs/development/documentation.md b/docs/docs/development/documentation.md index 56c4d27699b..169642ba714 100644 --- a/docs/docs/development/documentation.md +++ b/docs/docs/development/documentation.md @@ -5,7 +5,7 @@ sidebar_label: Documentation This document outlines how to test your documentation changes locally and prepare the changes for a pull request. -The documentation is written with [Docusaurus](https://docusaurus.io/). The ZMK source code has all of the necessary Docusaurus dependencies included but referencing their documentation can be helpful at times. +The documentation is written with [Docusaurus](https://docusaurus.io/). The ZMK source code has all of the necessary Docusaurus dependencies included, but referencing their documentation can be helpful at times. The general process for updating the ZMK documentation is: @@ -52,6 +52,12 @@ The check commands can be run with the following procedure in a terminal that's If any of the above steps throw an error, they need to be addressed and all of the checks re-run prior to submitting a pull request. ::: +:::note +The documentation uses American English spelling and grammar conventions. Title case is used for the first three heading levels, with sentence case used beyond that. + +Please make sure your changes conform to these conventions - prettier and lint are unfortunately unable to do this automatically. +::: + ## Submitting a Pull Request Once the above sections are complete the documentation updates are ready to submit as a pull request. diff --git a/docs/docs/development/ide-integration.mdx b/docs/docs/development/ide-integration.mdx index 87a5a4caf55..cee12f4b3bf 100644 --- a/docs/docs/development/ide-integration.mdx +++ b/docs/docs/development/ide-integration.mdx @@ -51,7 +51,7 @@ Change these options: If you are developing inside a Docker container, set the IntelliSense mode to `linux-gcc-arm` regardless of the host operating system. -#### Compiler Path +#### Compiler path Open VS Code's integrated terminal and run the following command: @@ -79,7 +79,7 @@ If you are building for an platform other than ARM, replace `/arm-zephyr-eabi/bi /home/marvin/.local/zephyr-sdk-0.15.2/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc ``` -#### Compiler Commands Path +#### Compiler commands path When building with all default options, the path to the compilation database file is `${workspaceFolder}/app/build/compile_commands.json` as shown in the table above, diff --git a/docs/docs/development/new-behavior.mdx b/docs/docs/development/new-behavior.mdx index 914abf52063..dca19288dc0 100644 --- a/docs/docs/development/new-behavior.mdx +++ b/docs/docs/development/new-behavior.mdx @@ -35,7 +35,7 @@ The following resources are provided for those seeking further understanding: ## Creating the Behavior -### Creating the devicetree binding (`.yaml`) +### Creating the Devicetree Binding (`.yaml`) The properties of the behavior are listed in the behavior's devicetree binding, which comes in the form of a `.yaml` file. Devicetree bindings are stored in the directory `app/dts/bindings/behaviors/` and are labelled in lowercase, beginning with the prefix `zmk,behavior-`, and ending with the behavior's name, using dashes to separate multiple words. For example, the directory for the hold-tap's devicetree binding would be located at `app/dts/bindings/behaviors/zmk,behavior-hold-tap.yaml`, which is shown below as a reference: @@ -119,7 +119,7 @@ These are additional variables required to configure a particular instance of a For more information on additional `properties`, refer to [Zephyr's documentation on Devicetree bindings](https://docs.zephyrproject.org/3.5.0/build/dts/bindings-syntax.html#properties). ::: -### Creating the driver (`.c`) +### Creating the Driver (`.c`) :::info Developing drivers for behaviors in ZMK makes extensive use of the Zephyr Devicetree API and Device Driver Model. Links to the Zephyr Project Documentation for both of these concepts can be found below: @@ -203,7 +203,7 @@ The dependencies required for any ZMK behavior are: Other common dependencies include `zmk/keymap.h`, which allows behaviors to access layer information and extract behavior bindings from keymaps, and `zmk/event_manager.h` which is detailed below. -##### ZMK Event Manager +##### ZMK event manager Including `zmk/event_manager.h` is required for the following dependencies to function properly. @@ -213,7 +213,7 @@ Including `zmk/event_manager.h` is required for the following dependencies to fu Events can be used similarly to hardware interrupts, through the use of [listeners](#listeners-and-subscriptions). -###### Listeners and Subscriptions +###### Listeners and subscriptions The condensed form of lines 192-225 of the tap-dance driver, shown below, does an excellent job of showcasing the function of listeners and subscriptions with respect to the [ZMK Event Manager](#zmk-event-manager). @@ -279,11 +279,11 @@ Note that in the hold-tap example, the instance number, `0`, has been replaced b Behaviors also require the following parameters of `BEHAVIOR_DT_INST_DEFINE` to be changed: -##### Initialization Function +##### Initialization function Comes in the form `static int _init(const struct device *dev)`. Initialization functions preconfigure any data, like resetting timers and position for hold-taps and tap-dances. All initialization functions `return 0;` once complete. -##### API Structure +##### API structure Comes in the form `static const struct behavior_driver_api _driver_api)`. Common items to include in the API Structure are: @@ -297,7 +297,7 @@ Comes in the form `static const struct behavior_driver_api _drive For unibody keyboards, all locality values perform the same as `BEHAVIOR_LOCALITY_GLOBAL`. ::: -##### Data Pointers (Optional) +##### Data pointers (optional) The data `struct` stores additional data required for **each new instance** of the behavior. Regardless of the instance number, `n`, `behavior__data_##n` is typically initialized as an empty `struct`. The data respective to each instance of the behavior can be accessed in functions like [`on__binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event)`](#dependencies) by extracting the behavior device from the keybind like so: @@ -310,7 +310,7 @@ The variables stored inside the data `struct`, `data`, can be then modified as n The fourth cell of `BEHAVIOR_DT_INST_DEFINE` can be set to `NULL` instead if instance-specific data is not required. -##### Configuration Pointers (Optional) +##### Configuration pointers (optional) The configuration `struct` stores the properties declared from the behavior's `.yaml` for **each new instance** of the behavior. As seen in the `#define KP_INST(n)` of the hold-tap example, the configuration `struct`, `behavior__config_##n`, for each instance number, `n`, can be initialized using the [Zephyr Devicetree Instance-based APIs](https://docs.zephyrproject.org/3.5.0/build/dts/api/api.html#instance-based-apis), which extract the values from the `properties` of each instance of the [devicetree binding](#creating-the-devicetree-binding-yaml) from a user's keymap or [predefined use-case `.dtsi` files](#defining-common-use-cases-for-the-behavior-dtsi-optional) stored in `app/dts/behaviors/`. We illustrate this further by comparing the [`#define KP_INST(n)` from the hold-tap driver](#behavior_dt_inst_define) and the [`properties` of the hold-tap devicetree binding.](#creating-the-devicetree-binding-yaml) @@ -320,7 +320,7 @@ The fifth cell of `BEHAVIOR_DT_INST_DEFINE` can be set to `NULL` instead if inst Remember that `.c` files should be formatted according to `clang-format` to ensure that checks run smoothly once the pull request is submitted. ::: -### Updating `app/CmakeLists.txt` to include the new driver +### Updating `app/CmakeLists.txt` to Include the New Driver Most behavior drivers' are invoked according to the central half's [locality](#api-structure), and are therefore stored after the line `if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)` in the form, `target_sources(app PRIVATE src/behaviors/.c)`, as shown below. @@ -353,7 +353,7 @@ For behaviors that do not require central locality, the following options for up - Behavior applies to _only_ peripheral half of split keyboard: place `target_sources(app PRIVATE .c)` after `if (CONFIG_ZMK_SPLIT AND (NOT CONFIG_ZMK_SPLIT_ROLE_CENTRAL))` - Behavior requires certain condition in a keyboard's `.conf` file to be met: use `target_sources_ifdef(CONFIG_ app PRIVATE .c)` instead of `target_sources(.c)` -### Defining common use-cases for the behavior (`.dtsi`) (Optional) +### Defining Common Use-Cases for the Behavior (`.dtsi`) (Optional) `.dtsi` files, found in the directory `app/dts/behaviors/`, are only necessary for behaviors with more common use-cases. A common example is the mod-tap (`&mt`), which is a predefined type of hold-tap that takes a modifier key as the hold parameter and another key as the tap parameter. @@ -422,7 +422,7 @@ After creating the `.dtsi` from above, update `app/dts/behaviors.dtsi` to includ #include ``` -## Testing changes locally +## Testing Changes Locally Create a new folder in `app/tests/` to develop virtual test sets for all common use cases of the behavior. Behaviors should be tested thoroughly on both virtual testing environments using `west test` and real hardware. @@ -437,7 +437,7 @@ Zephyr currently does not support logging over Bluetooth, so any use of the seri ::: -## Documenting behavior functionality +## Documenting Behavior Functionality Consider the following prompts when writing documentation for new behaviors: @@ -453,7 +453,7 @@ Consider also including visual aids alongside written documentation if it adds c See [Documentation](documentation.md) for more information on writing, testing, and formatting ZMK documentation. ::: -## Submitting a pull request +## Submitting a Pull Request Once the above sections are complete, the behavior is almost ready to submit as a pull request. New [devicetree bindings](#creating-the-devicetree-binding-yaml), new [drivers](#creating-the-driver-c), and [predefined use-cases](#defining-common-use-cases-for-the-behavior-dtsi-optional) of the new behavior must contain the appropriate copyright headers, which can be copied and pasted from the tabs below. diff --git a/docs/docs/development/new-shield.mdx b/docs/docs/development/new-shield.mdx index a542fc84995..d48e0d1de97 100644 --- a/docs/docs/development/new-shield.mdx +++ b/docs/docs/development/new-shield.mdx @@ -554,7 +554,7 @@ Add additional bindings as necessary to match the default number of encoders on ### GitHub Actions -Using GitHub Actions to build your new firmware can save you from doing any local [development setup](./setup.mdx), +Using GitHub Actions to build your new firmware can save you from doing any local [development setup](./setup/index.md), at the expense of a longer feedback loop if there are issues. To push your changes and trigger a build: - Add all your pending changes with `git add .` @@ -566,7 +566,7 @@ Once pushed, click on the "Actions" tab of the repo you created in the first ste ### Local Build :::note -To build locally, be sure you've followed the [development setup](./setup.mdx) guide first. +To build locally, be sure you've followed the [development setup](./setup/index.md) guide first. ::: Once you've fully created the new keyboard shield definition, diff --git a/docs/docs/development/setup.mdx b/docs/docs/development/setup.mdx deleted file mode 100644 index bc275804cf7..00000000000 --- a/docs/docs/development/setup.mdx +++ /dev/null @@ -1,322 +0,0 @@ ---- -title: Toolchain Setup -sidebar_label: Toolchain Setup ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -export const OsTabs = (props) => ( - - {/* eslint-disable-next-line */} - {props.children} - -); - -This guide will show you how to set up a development environment for building ZMK locally. - -## Install Dependencies - -Click the operating system you are using. (The VS Code & Docker option can be used on any OS.) - - - - -This option use the same [Docker image which is used by the GitHub action](https://github.com/zmkfirmware/zmk-docker) for local development. Beyond the benefits of [dev/prod parity](https://12factor.net/dev-prod-parity), this approach is also the easiest to set up. No toolchain or dependencies are necessary when using Docker; the container image you'll be using already has the toolchain installed and set up to use. - -1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system. -2. Install [Visual Studio Code](https://code.visualstudio.com/) -3. Install the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) - -:::info -The docker container already includes `west`. Skip past the following section to [Get Source Code](#get-source-code). -::: - - - - -Open Zephyr's [Getting Started Guide](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html) and follow the instructions under these sections: - -- [Select and Update OS](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#select-and-update-os) -- [Install Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-dependencies) -- [Install Zephyr SDK](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-zephyr-sdk) - -Return to this guide once you are finished with each section. - - - - -Open Zephyr's [Getting Started Guide](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html) and follow the instructions under these sections: - -- [Select and Update OS](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#select-and-update-os) -- [Install Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-dependencies) -- [Install Zephyr SDK](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-zephyr-sdk) - -Return to this guide once you are finished with each section. - -`dfu-util` is required to flash devices that use DFU, but there is currently no maintained package for it on Chocolatey. [QMK Toolbox](https://github.com/qmk/qmk_toolbox) contains a working version of it though. - - - - -Open Zephyr's [Getting Started Guide](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html) and follow the instructions under these sections: - -- [Select and Update OS](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#select-and-update-os) -- [Install Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-dependencies) -- [Install Zephyr SDK](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-zephyr-sdk) - -Return to this guide once you are finished with each section. - - - - -#### Install Base Dependencies - -Open Zephyr's [Getting Started Guide](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html) and follow the instructions for Ubuntu under these sections: - -- [Select and Update OS](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#select-and-update-os) -- [Install Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-dependencies) - -Return to this guide once you are finished with each section. - -#### Install Cross-Compile Toolchain - -Because Raspberry OS runs on the same architecture (but different ABI) as ARM keyboard MCUs, the operating system's installed [cross compilers](https://docs.zephyrproject.org/3.5.0/develop/toolchains/other_x_compilers.html) can be used to target the different ABI. Building for non-ARM MCUs has not been tested. - -First, the cross compiler should be installed: - -```sh -sudo apt install gcc-arm-none-eabi -``` - -Next, we'll configure Zephyr with some [environment variables](https://docs.zephyrproject.org/3.5.0/develop/env_vars.html#env-vars) needed to find the cross compiler. Create a file named `~/.zephyrrc` if it doesn't exist, and add these lines to it: - -```sh -export ZEPHYR_TOOLCHAIN_VARIANT=cross-compile -export CROSS_COMPILE=/usr/bin/arm-none-eabi- -``` - - - - -Follow Zephyr's [Install Linux Host Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/installation_linux.html) documentation for Fedora. - - - - -### Install West - -`west` is the [Zephyr® Project's meta-tool](https://docs.zephyrproject.org/3.5.0/develop/west/index.html) used to configure and build Zephyr OS applications. - -West can be installed by using the `pip` python package manager. The [Zephyr™ instructions](https://docs.zephyrproject.org/3.5.0/develop/west/install.html) are summarized here: - - - - -Install west: - -```sh -pip3 install --user -U west -``` - -Verify that west is installed: - -```sh -west --version -``` - -This should print a message like "West version: v0.14.0". If it prints an error instead, make sure `~/.local/bin` is on your `PATH` environment variable. You can add it with these commands: - -```sh -echo 'export PATH=~/.local/bin:"$PATH"' >> ~/.bashrc -source ~/.bashrc -``` - - - - -Install west: - -```sh -pip3 install -U west -``` - -Verify that west is installed: - -```sh -west --version -``` - -This should print a message like "West version: v0.14.0". If it prints an error instead, make sure that the Python scripts directory is on your `PATH` environment variable. You can add it by opening a PowerShell window and running the following commands: - -```powershell -$Scripts = python -c "import sysconfig; print(sysconfig.get_path('scripts'))" -$Path = [Environment]::GetEnvironmentVariable('PATH', 'User') -[Environment]::SetEnvironmentVariable('PATH', "$Path;$Scripts", 'User') -$env:PATH += ";$Scripts" -``` - - - - -Install west: - -```sh -pip3 install -U west -``` - - - - -## Get Source Code - -Next, you'll need to clone the ZMK source repository if you haven't already. Navigate to the folder you would like to place your `zmk` directory in and run the following command: - -``` -git clone https://github.com/zmkfirmware/zmk.git -``` - -## Initialize & Update Zephyr Workspace - -Since ZMK is built as a Zephyr™ application, the next step is -to use `west` to initialize and update your workspace. The ZMK -Zephyr™ application is in the `app/` source directory: - -### Step into the repository - - - - -```sh -cd zmk -``` - - - - -```sh -cd zmk -``` - - - - -```sh -cd zmk -``` - - - - -```sh -cd zmk -``` - - - - -```sh -cd zmk -``` - - - - - -Open the `zmk` checkout folder in VS Code. The repository includes a configuration for containerized development, so an alert will pop up: - -![VS Code Dev Container Configuration Alert](../assets/dev-setup/vscode_devcontainer.png) - -Click `Reopen in Container` in order to reopen the VS Code with the running container. - -The first time you do this on your machine, it will pull the docker image down from the registry and build the container. Subsequent launches are much faster! - -:::warning -All subsequent steps must be performed from the VS Code terminal _inside_ the container. -::: - - - - -### Initialize the Application - -```sh -west init -l app/ -``` - -### Update to Fetch Modules - -```sh -west update -``` - -:::tip -This step pulls down quite a bit of tooling. Go grab a cup of coffee, it can take 10-15 minutes even on a good internet connection! -::: - -:::info -If you're using Docker, you're done with setup! You must restart the container at this point. The easiest way to do so is to close the VS Code window, verify that the container has stopped in Docker Dashboard, and reopen the container with VS Code. - -Once your container is restarted, proceed to [Building and Flashing](development/build-flash.mdx). -::: - -### Export Zephyr CMake package - -This allows CMake to load the code needed to build ZMK. - -```sh -west zephyr-export -``` - -### Install Zephyr Python Dependencies - -Some additional Python dependencies are listed in Zephyr's `scripts/requirements.txt` file. - - - - -```sh -pip3 install --user -r zephyr/scripts/requirements.txt -``` - - - - -```sh -pip3 install -r zephyr/scripts/requirements.txt -``` - - - - -```sh -pip3 install -r zephyr/scripts/requirements.txt -``` - - - diff --git a/docs/docs/development/setup/docker.md b/docs/docs/development/setup/docker.md new file mode 100644 index 00000000000..767331e409f --- /dev/null +++ b/docs/docs/development/setup/docker.md @@ -0,0 +1,53 @@ +--- +title: Docker +sidebar_label: Docker +--- + +:::note +Currently the Docker approach is only documented for [VS Code](https://github.com/microsoft/vscode) (not [Code OSS](https://github.com/microsoft/vscode/wiki/Differences-between-the-repository-and-Visual-Studio-Code)). While it can be replicated using [devcontainers](https://containers.dev/) this is not documented yet - contributions are welcome! +::: + +### Source Code + +First, you'll need to clone the ZMK source repository if you haven't already. Open a terminal and navigate to the folder you would like to place your `zmk` directory in, then run the following command: + +```sh +git clone https://github.com/zmkfirmware/zmk.git +``` + +### Installing Development Tools + +1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system. +2. Install [VS Code](https://code.visualstudio.com/). +3. Install the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). + +### Initialize & Update Zephyr Workspace + +Open the `zmk` checkout folder in VS Code. The repository includes a configuration for containerized development, so an alert will pop up: + +![VS Code Dev Container Configuration Alert](../../assets/dev-setup/vscode_devcontainer.png) + +Click `Reopen in Container` in order to reopen the VS Code with the running container. If the alert fails to pop up or you accidentally close it, you can perform the same action by pressing `ctrl+shift+p` and selecting `Remote: Show Remote Menu`. + +The first time you do this on your machine, it will pull the docker image down from the registry and build the container. Subsequent launches are much faster! + +:::caution +The following step and any future [build commands](../build-flash.mdx) must be executed from the VS Code terminal _inside_ the container. +::: + +Initialize the application and update to fetch modules, including Zephyr: + +```sh +west init -l app/ +west update +``` + +:::tip +This step pulls down quite a bit of tooling, be patient! +::: + +:::info +You must restart the container at this point. The easiest way to do so is to close the VS Code window, verify that the container has stopped in Docker Dashboard, and reopen the container with VS Code. + +Your setup is complete once your container has restarted. +::: diff --git a/docs/docs/development/setup/index.md b/docs/docs/development/setup/index.md new file mode 100644 index 00000000000..5c795fa22c7 --- /dev/null +++ b/docs/docs/development/setup/index.md @@ -0,0 +1,20 @@ +--- +title: Getting Started +sidebar_label: Getting Started +--- + +:::tip +We recommend reading through the setup process before following it step by step, to ensure that you are happy with installing the required dependencies. +::: + +## Environment Setup + +There are two ways to set up the ZMK development environment: + +- [Docker](/docs/development/setup/docker): \ + A self-contained development environment. It uses the same [Docker image which is used by the GitHub action](https://github.com/zmkfirmware/zmk-docker) for local development. Beyond the benefits of [dev/prod parity](https://12factor.net/dev-prod-parity), this approach may be easier to set up for some operating systems. No toolchain or dependencies are necessary when using Docker; the container image has the toolchain installed and set up to use. + +- [Native](/docs/development/setup/native):\ + This uses your operating system directly. Usually runs slightly faster than the Docker approach, and can be preferable for users who already have the dependencies on their system. + +Please see the [Docker](/docs/development/setup/docker) instructions or [native](/docs/development/setup/native) instructions to continue setup. diff --git a/docs/docs/development/setup/native.mdx b/docs/docs/development/setup/native.mdx new file mode 100644 index 00000000000..40c1bbed6ee --- /dev/null +++ b/docs/docs/development/setup/native.mdx @@ -0,0 +1,353 @@ +--- +title: Native Setup +sidebar_label: Native +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +export const OsTabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + +export const OsNoteTabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + +export const EnvTabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + +export const WinTermTabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + +## 1. Install Zephyr Dependencies + +Open Zephyr's [Getting Started Guide](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html) and follow the instructions under these sections: + +- [Select and Update OS](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#select-and-update-os) +- [Install Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-dependencies) + +:::info +Zephyr's [Install Linux Host Dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/installation_linux.html) page may be of use for users of Linux distributions which are not based on Ubuntu. +::: + +## 2. Source Code + +Next, you'll need to clone the ZMK source repository if you haven't already. Open a terminal and navigate to the folder you would like to place your `zmk` directory in, then run the following command: + +```sh +git clone https://github.com/zmkfirmware/zmk.git +``` + +Then step into the repository. + +```sh +cd zmk +``` + +## 3. Get Zephyr and install Python dependencies + +:::note +These steps are very similar to Zephyr's [Get Zephyr and install Python dependencies](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#get-zephyr-and-install-python-dependencies) instructions, but specialized for ZMK. +::: + + + + + + +1. Use `apt` to install Python `venv` package: + +```sh +sudo apt install python3-venv +``` + +2. Create a new virtual environment and activate it: + +```sh +python3 -m venv .venv +source .venv/bin/activate +``` + + + + +1. Create a new virtual environment: + +```sh +python -m venv .venv +``` + +2. Activate the virtual environment: + + + + +```sh +.venv\Scripts\activate.bat +``` + + + + + +```powershell +.venv\Scripts\Activate.ps1 +``` + + + + + + + + +1. Create a new virtual environment: + +```sh +python3 -m venv .venv +``` + +2. Activate the virtual environment: + +```sh +source .venv/bin/activate +``` + + + + +Once activated your shell will be prefixed with `(.venv)`. The virtual environment can be deactivated at any time by running `deactivate`. + +:::note +Remember to activate the virtual environment every time you start working. +::: + +4. Install west: + +```sh +pip install west +``` + +5. Initialize the application and update to fetch modules, including Zephyr: + +```sh +west init -l app/ +west update +``` + +:::tip +This step pulls down quite a bit of tooling, be patient! +::: + +6. Export a [Zephyr CMake package](https://docs.zephyrproject.org/3.5.0/build/zephyr_cmake_package.html#cmake-pkg). This allows CMake to automatically load boilerplate code required for building Zephyr applications. + +```sh +west zephyr-export +``` + +7. Install the additional dependencies found in Zephyr's `requirements-base.txt`: + +```sh +pip install -r zephyr/scripts/requirements-base.txt +``` + + + + + +1. Install `west`: + +```sh +pip3 install --user -U west +``` + +:::note +You need `~/.local/bin` to be on your `PATH` environment variable; verify that it is by running + +```sh +west --version +``` + +If this prints an error rather than a `west` version number, then add `~/.local/bin` to your `PATH`: + +```sh +echo 'export PATH=~/.local/bin:"$PATH"' >> ~/.bashrc +source ~/.bashrc +``` + +::: + + + + +1. Install `west`: + +```sh +pip install -U west +``` + +:::note +You need the Python scripts directory to be on your PATH environment variable; verify that it is by running + +```sh +west --version +``` + +If this prints an error rather than a `west` version number, then add said directory to your `PATH` with PowerShell: + +```powershell +$Scripts = python -c "import sysconfig; print(sysconfig.get_path('scripts'))" +$Path = [Environment]::GetEnvironmentVariable('PATH', 'User') +[Environment]::SetEnvironmentVariable('PATH', "$Path;$Scripts", 'User') +$env:PATH += ";$Scripts" +``` + +::: + + + + + +1. Install `west`: + +```sh +pip3 install -U west +``` + + + + +2. Initialize the application and update to fetch modules, including Zephyr: + +```sh +west init -l app/ +west update +``` + +:::tip +This step pulls down quite a bit of tooling, be patient! +::: + +3. Export a [Zephyr CMake package](https://docs.zephyrproject.org/3.5.0/build/zephyr_cmake_package.html#cmake-pkg). This allows CMake to automatically load boilerplate code required for building Zephyr applications. + +```sh +west zephyr-export +``` + + + + +4. Install the additional dependencies found in Zephyr's `requirements-base.txt`: + +```sh +pip3 install --user -r zephyr/scripts/requirements-base.txt +``` + + + + + +4. Install the additional dependencies found in Zephyr's `requirements-base.txt`: + +```sh +pip install -r zephyr/scripts/requirements-base.txt +``` + + + + +4. Install the additional dependencies found in Zephyr's `requirements-base.txt`. + +```sh +pip3 install -r zephyr/scripts/requirements-base.txt +``` + + + + + + +## 4. Install Zephyr SDK + +Return to Zephyr's Getting Started Guide and [Install Zephyr SDK](https://docs.zephyrproject.org/3.5.0/develop/getting_started/index.html#install-zephyr-sdk). + +### OS-Specific Notes + + + + `dfu-util` is required to flash devices that use DFU, but there is currently + no maintained package for it on Chocolatey. [QMK + Toolbox](https://github.com/qmk/qmk_toolbox) contains a working version of it + though. + + + +#### Install cross-compile toolchain + +Because Raspberry OS runs on the same architecture (but different ABI) as ARM keyboard MCUs, the operating system's installed [cross compilers](https://docs.zephyrproject.org/3.5.0/develop/toolchains/other_x_compilers.html) can be used to target the different ABI. Building for non-ARM MCUs has not been tested. + +First, the cross compiler should be installed: + +```sh +sudo apt install gcc-arm-none-eabi +``` + +Next, we'll configure Zephyr with some [environment variables](https://docs.zephyrproject.org/3.5.0/develop/env_vars.html#env-vars) needed to find the cross compiler. Create a file named `~/.zephyrrc` if it doesn't exist, and add these lines to it: + +```sh +export ZEPHYR_TOOLCHAIN_VARIANT=cross-compile +export CROSS_COMPILE=/usr/bin/arm-none-eabi- +``` + + + + +Your setup is now complete. diff --git a/docs/docs/development/usb-logging.mdx b/docs/docs/development/usb-logging.mdx index cb9508a2be0..b7c3d233820 100644 --- a/docs/docs/development/usb-logging.mdx +++ b/docs/docs/development/usb-logging.mdx @@ -18,26 +18,33 @@ It is recommended to only enable logging when needed, and not leaving it on by d ::: -## Kconfig +## USB Logging Snippet -The `CONFIG_ZMK_USB_LOGGING` Kconfig enables USB logging. This can be set at the keyboard level, typically in the `config/.conf` -file if you are using a [user config repository](user-setup.mdx). It can also be enabled at the ZMK level using the `app/prj.conf` file, or other -search locations described in the [configuration overview](config/index.md#config-file-locations). +The `zmk-usb-logging` snippet is used to enable logging. -Logging can be further configured using Kconfig described in [the Zephyr documentation](https://docs.zephyrproject.org/3.5.0/services/logging/index.html). -For instance, setting `CONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS` to a large value such as `8000` might help catch issues that happen near keyboard -boot, before you can connect to view the logs. +If using GitHub Actions to build your firmware, enabling logging +requires adding a `snippet: zmk-usb-logging` to your `build.yaml` file for any build you want logging enabled, e.g. -:::note -In Github Actions, you can check the ` Kconfig file` step output to verify the options above have been enabled -for you successfully. -::: +```yaml +--- +include: + - board: nice_nano_v2 + shield: corne_left + snippet: zmk-usb-logging +``` -```ini -# Turn on logging, and set ZMK logging to debug output -CONFIG_ZMK_USB_LOGGING=y +When building locally, the `-S`/`--snippet` flag can be passed to `west build` to enable the snippet, e.g. + +```sh +west build -b nice_nano_v2 -S zmk-usb-logging -- -DSHIELD="corne_left" ``` +### Additional Config + +Logging can be further configured using Kconfig described in [the Zephyr documentation](https://docs.zephyrproject.org/3.5.0/services/logging/index.html). +For instance, setting `CONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS` to a large value such as `8000` might help catch issues that happen near keyboard +boot, before you can connect to view the logs. + ## Viewing Logs After flashing the updated ZMK image, the board should expose a USB CDC ACM device that you can connect to and view the logs. @@ -89,27 +96,29 @@ From there, you should see the various log messages from ZMK and Zephyr, dependi Standard boards such as the nice!nano and Seeeduino XIAO family have the necessary configuration for logging already added, however if you are developing your own standalone board you may wish to add the ability to use USB logging in the future. -To add USB logging to a board you need to define the USB CDC ACM device that the serial output gets piped to, as well as adding the console in the `chosen` node inside `.dts`. +To do so, you need to follow the upstream Zephyr [`cdc-acm-console` snippet requirements](https://docs.zephyrproject.org/3.5.0/snippets/cdc-acm-console/README.html#requirements) steps. -Inside the USB device (`&usbd`), add the CDC ACM node: +Usually, this just requires ensuring that the USB node has been tagged with the `zephyr_udc0` label, e.g. ```dts -&usbd { +zephyr_udc0: &usbd { status = "okay"; - cdc_acm_uart: cdc_acm_uart { - compatible = "zephyr,cdc-acm-uart"; - }; }; ``` -Then you can add the `zephyr,console` binding in the `chosen` node: +## Enabling Logging on Older Boards -```dts -/ { - chosen { - ... - zephyr,console = &cdc_acm_uart; - }; - ... -}; +Previously, enabling logging required setting the `CONFIG_ZMK_USB_LOGGING` Kconfig symbol. If for whatever reason +a custom board definition does not support the new `zmk-usb-logging` snippet, you can try setting this symbol at the keyboard level, typically in the `config/.conf` +file if you are using a [user config repository](user-setup.mdx). It can also be enabled at the ZMK level using the `app/prj.conf` file, or other +search locations described in the [configuration overview](config/index.md#config-file-locations). + +:::note +In Github Actions, you can check the ` Kconfig file` step output to verify the options above have been enabled +for you successfully. +::: + +```ini +# Turn on logging, and set ZMK logging to debug output +CONFIG_ZMK_USB_LOGGING=y ``` diff --git a/docs/docs/features/backlight.mdx b/docs/docs/features/backlight.mdx index 4c47305783a..5debc375010 100644 --- a/docs/docs/features/backlight.mdx +++ b/docs/docs/features/backlight.mdx @@ -34,7 +34,7 @@ There are various Kconfig options used to configure the backlight feature. These | `CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE` | Turn off backlight when keyboard goes into idle state | n | | `CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB` | Turn off backlight when USB is disconnected | n | -## Adding Backlight to a board or a shield +## Adding Backlight to a Board or a Shield -pinctrl.dtsi` file if it does not already exist, and include it at the beginning of the `.dts` file. `CONFIG_PINCTRL=y` must be added to to `_defconfig` if it isn't already enabled. +Create a `-pinctrl.dtsi` file if it does not already exist, and include it at the beginning of the `.dts` file. `CONFIG_PINCTRL=y` must be added to `_defconfig` if it isn't already enabled. The pinctrl file has a `&pinctrl` node that encompasses all pinctrl settings, including I2C or SPI peripherals (e.g. WS2812 LEDs, Battery fuel gauges): @@ -208,7 +208,7 @@ Finally you need to add backlight to the `chosen` element of the root devicetree -### Multiple backlight LEDs +### Multiple Backlight LEDs It is possible to control multiple backlight LEDs at the same time. This is useful if, for example, you have a Caps Lock LED connected to a different pin and you want it to be part of the backlight. diff --git a/docs/docs/features/beta-testing.mdx b/docs/docs/features/beta-testing.mdx index 148005cea67..1a8d28633cb 100644 --- a/docs/docs/features/beta-testing.mdx +++ b/docs/docs/features/beta-testing.mdx @@ -28,7 +28,7 @@ branch and create the pull request. ![Repository URL](../assets/features/beta-testing/repo-branch.png) -## Testing features +## Testing Features Testing features will require you to modify the `west.yml` file. You will need to add a new remote for the pull request you would like to test, and change the selected remote and revision (or branch) for the `zmk` project. diff --git a/docs/docs/features/bluetooth.md b/docs/docs/features/bluetooth.md index 28ba21367d1..d148acd82db 100644 --- a/docs/docs/features/bluetooth.md +++ b/docs/docs/features/bluetooth.md @@ -47,52 +47,4 @@ Firmware changes that would modify the descriptor include the following: While the descriptor refresh happens on boot for USB, hosts will frequently cache this descriptor for BLE devices. In order to refresh this cache, you need to remove the keyboard from the host device, clear the profile associated with the host on the keyboard, then pair again. -For Windows systems you might need to follow the instructions in [this troubleshooting section](#windows-connected-but-not-working) below. - -## Troubleshooting - -### Connectivity Issues - -Some users may experience a poor connection between the keyboard and the host. This might be due to poor quality BLE hardware, a metal enclosure on the keyboard or host, or the distance between them. Increasing the transmit power of the keyboard's BLE radio may reduce the severity of this problem. To do this, set the `CONFIG_BT_CTLR_TX_PWR_PLUS_8` configuration value in the `.conf` file of your user config directory as such: - -```ini -CONFIG_BT_CTLR_TX_PWR_PLUS_8=y -``` - -For the `nRF52840`, the value `PLUS_8` can be set to any multiple of four between `MINUS_20` and `PLUS_8`. The default value for this config is `0`, but if you are having connection issues it is recommended to set it to `PLUS_8` because the power consumption difference is negligible. For more information on changing the transmit power of your BLE device, please refer to [the Zephyr docs.](https://docs.zephyrproject.org/3.5.0/kconfig.html#CONFIG_BT_CTLR_TX_PWR) - -:::info -This setting can also improve the connection strength between the keyboard halves for split keyboards. -::: - -### Using bluetooth output with USB power - -If you want to test bluetooth output on your keyboard and are powering it through the USB connection rather than a battery, you will be able to pair with a host device but may not see keystrokes sent. In this case you need to use the [output selection behavior](../behaviors/outputs.md) to prefer sending keystrokes over bluetooth rather than USB. This might be necessary even if you are not powering from a device capable of receiving USB inputs, such as a USB charger. - -### Issues with dual boot setups - -Since ZMK associates pairing/bond keys with hardware addresses of hosts, you cannot pair to two different operating systems in a dual boot system at the same time. -While you can find [documented workarounds](https://wiki.archlinux.org/title/bluetooth#Dual_boot_pairing) that involve copying pairing keys across operating systems and use both OS with a single profile, they can be fairly involved and should be followed with caution. - -### macOS Connected But Not Working - -If you attempt to pair a ZMK keyboard from macOS in a way that causes a bonding issue, macOS may report the keyboard as connected, but fail to actually work. If this occurs: - -1. Remove the keyboard from macOS using the Bluetooth control panel. -1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the macOS device is active, by pressing the correct keys for your particular keymap. -1. Try connecting again from macOS. - -### Windows Connected But Not Working - -Occasionally pairing the keyboard to a Windows device might result in a state where the keyboard is connected but does not send any key strokes. -If this occurs: - -1. Remove the keyboard from Windows using the Bluetooth settings. -1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the Windows device is active, by pressing the correct keys for your particular keymap. -1. Turn off Bluetooth from Windows settings, then turn it back on. -1. Pair the keyboard to the Windows device. - -If this doesn't help, try following the procedure above but replace step 3 with one of the following: - -- Restart the Windows device -- Open "Device Manager," turn on "Show hidden devices" from the "View" menu, then find and delete the keyboard under the "Bluetooth" item +For Windows systems you might need to follow the additional instructions in [the section on troubleshooting connection issues](troubleshooting/connection-issues.mdx#windows-connected-but-not-working). diff --git a/docs/docs/features/combos.md b/docs/docs/features/combos.md index 32f09c1c61e..63c57c38cc3 100644 --- a/docs/docs/features/combos.md +++ b/docs/docs/features/combos.md @@ -38,7 +38,7 @@ Key positions are numbered like the keys in your keymap, starting at 0. So, if t ::: -### Advanced usage +### Advanced Usage - Partially overlapping combos like `0 1` and `0 2` are supported. - Fully overlapping combos like `0 1` and `0 1 2` are supported. diff --git a/docs/docs/features/debouncing.md b/docs/docs/features/debouncing.md index 7d194efa061..31e00843211 100644 --- a/docs/docs/features/debouncing.md +++ b/docs/docs/features/debouncing.md @@ -40,7 +40,7 @@ CONFIG_ZMK_KSCAN_DEBOUNCE_PRESS_MS=3 CONFIG_ZMK_KSCAN_DEBOUNCE_RELEASE_MS=3 ``` -### Per-driver Options +### Per-Driver Options You can add these Devicetree properties to a kscan node to control debouncing for that instance of the driver. Values must be `<= 16383`. diff --git a/docs/docs/features/keymaps.mdx b/docs/docs/features/keymaps.mdx index 105ca794ab4..95df3e86842 100644 --- a/docs/docs/features/keymaps.mdx +++ b/docs/docs/features/keymaps.mdx @@ -96,7 +96,7 @@ The first defines the nodes for all the available behaviors in ZMK, which will b The second include brings in the defines for all the keycodes (e.g. `A`, `N1`, `C_PLAY`) and the modifiers (e.g. `LSHIFT`) used for various behavior bindings. -### Root devicetree Node +### Root Devicetree Node All the remaining keymap nodes will be nested inside of the root devicetree node, like so: diff --git a/docs/docs/features/soft-off.md b/docs/docs/features/soft-off.md index bd631f1b42e..7018afa08f2 100644 --- a/docs/docs/features/soft-off.md +++ b/docs/docs/features/soft-off.md @@ -101,7 +101,7 @@ To use the [soft off behavior](../behaviors/soft-off.md) outside of a keymap, ad The kscan sideband behavior driver will be used to trigger the [soft off behavior](../behaviors/soft-off.md) "out of band" from the normal keymap processing. To do so, it will decorate/wrap an underlying kscan driver. What kscan driver will vary for simple direct pin vs. matrix-integrated hardware combo. -#### Simple Direct Pin +#### Simple direct pin With a simple direct pin setup, the The [direct kscan](../config/kscan.md) driver can be used with a GPIO key, to make a small "side matrix": @@ -148,7 +148,7 @@ Here are the properties for the node: - The `compatible` property for the node must be `zmk,soft-off-wakeup-sources`. - The `wakeup-sources` property is a [phandle array](../config/index.md#devicetree-property-types) pointing to all the devices that should be enabled during the shutdown process to be sure they can later wake the keyboard. -#### Matrix-Integrated Hardware Combo +#### Matrix-integrated hardware combo For this case, you will supplement the existing kscan matrix, by adding the additional pin as another entry in the `row-gpios`/`col-gpios` for whichever pins are used to read the matrix state. For example, for an existing matrix like: diff --git a/docs/docs/features/underglow.md b/docs/docs/features/underglow.md index a32306caa0d..ba6c0092340 100644 --- a/docs/docs/features/underglow.md +++ b/docs/docs/features/underglow.md @@ -39,7 +39,7 @@ use Kconfig. If your board or shield does not have RGB underglow configured, refer to [Adding RGB Underglow to a Board](#adding-rgb-underglow-to-a-board). -### Modifying the number of LEDs +### Modifying the Number of LEDs A common issue when enabling underglow is that some of the installed LEDs do not illuminate. This can happen when a board's default underglow configuration accounts only for either the downward facing LEDs or the upward facing LEDs under each key. On a split keyboard, a good sign that this may be the problem is that the unilluminated LEDs on each half are symmetrical. @@ -64,7 +64,7 @@ If you have a shield with RGB underglow, you must add a `boards/` directory with Inside the `boards/` folder, you define a `.overlay` for each different board. For example, the Kyria shield has a `boards/nice_nano.overlay` file that defines the RGB underglow for the `nice_nano` board specifically. -### nRF52-based boards +### nRF52-Based Boards With nRF52 boards, you can just use `&spi3` and define the pins you want to use. @@ -128,7 +128,7 @@ If your board/shield uses LEDs that require the data sent in a different order, ::: -### Other boards +### Other Boards For other boards, you must select an SPI definition that has the `MOSI` pin as your data pin going to your LED strip. diff --git a/docs/docs/intro.md b/docs/docs/intro.md index da01e8297e7..e11eda71f02 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -46,7 +46,7 @@ ZMK is currently missing some features found in other popular firmware. This tab [^2]: Tap-Dances are limited to single and double-tap on BlueMicro [^1]: OLEDs are currently proof of concept in ZMK. -## Code Of Conduct +## Code of Conduct Please note that this project is released with a [Contributor Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). diff --git a/docs/docs/troubleshooting.md b/docs/docs/troubleshooting.md deleted file mode 100644 index 09efdecf770..00000000000 --- a/docs/docs/troubleshooting.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: Troubleshooting -sidebar_title: Troubleshooting ---- - -The following page provides suggestions for common errors that may occur during firmware compilation or other issues with keyboard usage. If the information provided is insufficient to resolve the issue, feel free to seek out help from the [ZMK Discord](https://zmk.dev/community/discord/invite). - -Please also see [the troubleshooting section](features/bluetooth.md#troubleshooting) under the Bluetooth feature page for issues related to bluetooth. - -### File Transfer Error - -Variations of the warnings shown below occur when flashing the `.uf2` onto the microcontroller. This is because the microcontroller resets itself before the OS receives confirmation that the file transfer is complete. Errors like this are normal and can generally be ignored. Verification of a functional board can be done by attempting to pair your newly flashed keyboard to your computer via Bluetooth or plugging in a USB cable if `ZMK_USB` is enabled in your Kconfig.defconfig. - -| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/windows.png) | -| :------------------------------------------------------------------------------: | -| An example of the file transfer error on Windows 10 | - -| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/linux.png) | -| :----------------------------------------------------------------------------: | -| An example of the file transfer error on Linux | - -| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/mac.png) | -| :--------------------------------------------------------------------------: | -| An example of the file transfer error on macOS | - -### macOS Ventura error - -macOS 13.0 (Ventura) Finder may report an error code 100093 when copying `.uf2` files into microcontrollers. This bug is limited to the operating system's Finder. You can work around it by copying on Terminal command line or use a third party file manager. Issue is fixed in macOS version 13.1. - -### macOS Sonoma error - -macOS 14.x (Sonoma) Finder may report an "Error code -36" when copying `.uf2` files into microcontrollers. A similar "fcopyfile failed: Input/output error" will also be reported when copying is performed using Terminal command line. These errors can be ignored because they are reported when the bootloader disconnects automatically after the uf2 file is copied successfully. - -### CMake Error - -An error along the lines of `CMake Error at (zmk directory)/zephyr/cmake/generic_toolchain.cmake:64 (include): include could not find load file:` during firmware compilation indicates that the Zephyr Environment Variables are not properly defined. -For more information, see [toolchain setup documentation](../docs/development/setup.mdx). - -### West Build Errors - -West build errors usually indicate syntax problems in the `.keymap` file during the compilation process. The following are some examples and root causes. - -:::note -If you are reviewing these errors in the GitHub Actions tab, they can be found in the `West Build` step of the build process. -::: - -#### Keymap error - -If you get an error stating `Keymap node not found, check a keymap is available and is has compatible = "zmk,keymap" set` this is an indication that the build process cannot find the keymap. Double check that the `.keymap` file is present and has been discovered by the build process. This can be checked by looking for a line in the build log stating `-- Using keymap file: /path/to/keymap/file/.keymap`. Inside the keymap file ensure the keymap node has `compatible = zmk,keymap` and it's not misspelled. For more information see the [Keymap](features/keymaps.mdx) and [Config](config/index.md) documentation. - -#### Devicetree errors - -A `devicetree error` followed by a reference to the line number on `.keymap` refers to an issue at the exact line position in that file. For example, below error message indicates a missing `;` at line 109 of the `cradio.keymap` file: - -``` -devicetree error: /__w/zmk-config/zmk-config/config/cradio.keymap:109 (column 4): parse error: expected ';' or ',' -``` - -A `devicetree error` followed by an `empty_file.c` reference with `lacks #binding-cells` string indicates possible problems with improper parameters for specific bindings: - -``` -devicetree error: lacks #binding-cells -``` - -This error can be triggered by incorrect binding syntax such as `&kp BT_SEL 0` instead of `&bt BT_SEL 0`. - -A `devicetree_generated.h` error that follows with an "undeclared here" string indicates a problem with key bindings, like behavior nodes (e.g. `&kp` or `&mt`) with incorrect number of parameters: - -``` -/__w/zmk-config/zmk-config/build/zephyr/include/generated/devicetree_generated.h:3756:145: error: 'DT_N_S_keymap_S_symbol_layer_P_bindings_IDX_12_PH_P_label' undeclared here (not in a function); did you mean 'DT_N_S_keymap_S_symbol_layer_P_bindings_IDX_16_PH'? -``` - -In this example, the error string `DT_N_S_keymap_S_symbol_layer_P_bindings_IDX_12_PH_P_label` indicates a problem with the key binding in position `12` in the `symbol_layer` of the keymap. - -:::info -Key positions are numbered starting from `0` at the top left key on the keymap, incrementing horizontally, row by row. -::: - -:::tip -A common mistake that leads to this error is to use [key press keycodes](behaviors/key-press.md) without the leading `&kp` binding. That is, having entries such as `SPACE` that should have been `&kp SPACE`. -::: - -### Split Keyboard Halves Unable to Pair - -Split keyboard halves will automatically pair with one another, but there are some cases where this breaks, and the pairing needs to be reset, for example: - -- Switching which halves are the central/peripheral. -- Replacing the controller for one of the halves. - -These issues can be resolved by flashing a settings reset firmware to both controllers. - -:::warning - -This procedure will erase all settings, such as Bluetooth profiles, output selection, RGB underglow color, etc. - -::: - -First, acquire the reset UF2 image file with one of the following options: - -#### Option 1: Build Reset UF2 in 'zmk-config' - -Find the `build.yaml` file in your `zmk-config` folder and add an additional settings reset build for the board used by your split keyboard. For example assuming that the config repo is setup for nice!nano v2 with Corne, append the `settings_reset` shield to the `build.yaml` file as follows: - -```yml -include: - - board: nice_nano_v2 - shield: corne_left - - board: nice_nano_v2 - shield: corne_right - - board: nice_nano_v2 - shield: settings_reset -``` - -Save the file, commit the changes and push them to GitHub. Download the new firmware zip file build by the latest GitHub Actions job. In it you will find an additional `settings_reset` UF2 image file. - -#### Option 2: Download Reset UF2 from ZMK's Workflow - -1. [Open the GitHub `Actions` tab and select the `Build` workflow](https://github.com/zmkfirmware/zmk/actions?query=workflow%3ABuild+branch%3Amain+event%3Apush). -1. Find one of the 'results' for which the core-coverage job was successfully run, indicated by a green checkmark in the core-coverage bubble like the image example below. -1. From the next page under "Artifacts", download and unzip the `-settings_reset-zmk` zip file for the UF2 image. - -| ![Successful core-coverage Job](../docs/assets/troubleshooting/splitpairing/corecoverage.png) | -| :-------------------------------------------------------------------------------------------: | -| An example of a successful core-coverage job which will produce a settings_reset firmware. | - -#### Reset Split Keyboard Procedure - -Perform the following steps to reset both halves of your split keyboard: - -1. Put each half of the split keyboard into bootloader mode. -1. Flash one of the halves of the split with the downloaded settings reset UF2 image. -1. Repeat step 2 with the other half of the split keyboard. -1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half). - -After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins for each of your keyboard's microcontrollers or pressing the reset buttons at the same time. - -Once this is done, you can remove/forget the keyboard on each host device and pair it again. - -:::info - -The settings reset firmware has Bluetooth disabled to prevent the two sides from automatically re-pairing until you are done resetting them both. You will not be able to pair your keyboard or see it in any Bluetooth device lists until you have flashed the normal firmware again. - -::: diff --git a/docs/docs/troubleshooting/building-issues.md b/docs/docs/troubleshooting/building-issues.md new file mode 100644 index 00000000000..e37d3934d79 --- /dev/null +++ b/docs/docs/troubleshooting/building-issues.md @@ -0,0 +1,54 @@ +--- +title: Building Issues +sidebar_label: Building Issues +description: Troubleshooting issues when compiling ZMK firmware. +--- + +## CMake Error + +An error along the lines of `CMake Error at (zmk directory)/zephyr/cmake/generic_toolchain.cmake:64 (include): include could not find load file:` during firmware compilation indicates that the Zephyr Environment Variables are not properly defined. +For more information, see [Zephyr's CMake Package](https://docs.zephyrproject.org/3.5.0/build/zephyr_cmake_package.html). + +## West Build Errors + +West build errors usually indicate syntax problems in the `.keymap` file during the compilation process. The following are some examples and root causes. + +:::note +If you are reviewing these errors in the GitHub Actions tab, they can be found in the `West Build` step of the build process. +::: + +### Keymap Error + +If you get an error stating `Keymap node not found, check a keymap is available and is has compatible = "zmk,keymap" set` this is an indication that the build process cannot find the keymap. Double check that the `.keymap` file is present and has been discovered by the build process. This can be checked by looking for a line in the build log stating `-- Using keymap file: /path/to/keymap/file/.keymap`. Inside the keymap file ensure the keymap node has `compatible = zmk,keymap` and it's not misspelled. For more information see the [Keymap](features/keymaps.mdx) and [Config](config/index.md) documentation. + +### Devicetree Errors + +A `devicetree error` followed by a reference to the line number on `.keymap` refers to an issue at the exact line position in that file. For example, below error message indicates a missing `;` at line 109 of the `cradio.keymap` file: + +``` +devicetree error: /__w/zmk-config/zmk-config/config/cradio.keymap:109 (column 4): parse error: expected ';' or ',' +``` + +A `devicetree error` followed by an `empty_file.c` reference with `lacks #binding-cells` string indicates possible problems with improper parameters for specific bindings: + +``` +devicetree error: lacks #binding-cells +``` + +This error can be triggered by incorrect binding syntax such as `&kp BT_SEL 0` instead of `&bt BT_SEL 0`. + +A `devicetree_generated.h` error that follows with an "undeclared here" string indicates a problem with key bindings, like behavior nodes (e.g. `&kp` or `&mt`) with incorrect number of parameters: + +``` +/__w/zmk-config/zmk-config/build/zephyr/include/generated/devicetree_generated.h:3756:145: error: 'DT_N_S_keymap_S_symbol_layer_P_bindings_IDX_12_PH_P_label' undeclared here (not in a function); did you mean 'DT_N_S_keymap_S_symbol_layer_P_bindings_IDX_16_PH'? +``` + +In this example, the error string `DT_N_S_keymap_S_symbol_layer_P_bindings_IDX_12_PH_P_label` indicates a problem with the key binding in position `12` in the `symbol_layer` of the keymap. + +:::info +Key positions are numbered starting from `0` at the top left key on the keymap, incrementing horizontally, row by row. +::: + +:::tip +A common mistake that leads to this error is to use [key press keycodes](behaviors/key-press.md) without the leading `&kp` binding. That is, having entries such as `SPACE` that should have been `&kp SPACE`. +::: diff --git a/docs/docs/troubleshooting/connection-issues.mdx b/docs/docs/troubleshooting/connection-issues.mdx new file mode 100644 index 00000000000..5fdd1c836a0 --- /dev/null +++ b/docs/docs/troubleshooting/connection-issues.mdx @@ -0,0 +1,140 @@ +--- +title: Connection Issues +sidebar_label: Connection Issues +description: Troubleshooting wireless connection issues of ZMK devices. +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +export const Uf2Tabs = (props) => ( + + {/* eslint-disable-next-line */} + {props.children} + + +); + +## Split Keyboard Halves Unable to Pair + +Split keyboard halves will automatically pair with one another, but there are some cases where this breaks, and the pairing needs to be reset, for example: + +- Switching which halves are the central/peripheral. +- Replacing the controller for one of the halves. + +These issues can be resolved by flashing a settings reset firmware to both controllers. + +:::warning + +This procedure will erase all settings, such as Bluetooth profiles, output selection, RGB underglow color, etc. + +::: + +### Acquiring a Reset UF2 + +First, acquire the reset UF2 image file with one of the following options: + + + + +Find the `build.yaml` file in your `zmk-config` folder and add an additional settings reset build for the board used by your split keyboard. For example assuming that the config repo is setup for nice!nano v2 with Corne, append the `settings_reset` shield to the `build.yaml` file as follows: + +```yml +include: + - board: nice_nano_v2 + shield: corne_left + - board: nice_nano_v2 + shield: corne_right + - board: nice_nano_v2 + shield: settings_reset +``` + +Save the file, commit the changes and push them to GitHub. Download the new firmware zip file build by the latest GitHub Actions job. In it you will find an additional `settings_reset` UF2 image file. + + + + +1. [Open the `Build` workflow](https://github.com/zmkfirmware/zmk/actions/workflows/build.yml?query=event%3Apush+branch%3Amain+is%3Asuccess) from the `Actions` tab of the ZMK GitHub repository. +1. Find one of the results for which the `core-coverage` job ran successfully, indicated by a green checkmark in the "core-coverage" bubble like the image example below. +1. From the next page under "Artifacts", download and unzip the `-settings_reset-zmk` zip file for the UF2 image. + +| ![Successful core-coverage Job](../../docs/assets/troubleshooting/splitpairing/corecoverage.png) | +| :----------------------------------------------------------------------------------------------: | +| An example of a successful core-coverage job which will produce a settings_reset firmware. | + + + + +### Reset Split Keyboard Procedure + +Perform the following steps to reset **_both_** halves of your split keyboard: + +1. Put each half of the split keyboard into bootloader mode. +1. Flash one of the halves of the split with the downloaded settings reset UF2 image. +1. Repeat step 2 with the other half of the split keyboard. +1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half). + +After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins for each of your keyboard's microcontrollers or pressing the reset buttons at the same time. + +Once this is done, you can remove/forget the keyboard on each host device and pair it again. + +:::info + +The settings reset firmware has Bluetooth disabled to prevent the two sides from automatically re-pairing until you are done resetting them both. You will not be able to pair your keyboard or see it in any Bluetooth device lists until you have flashed the normal firmware again. + +::: + +## Issues While Connected + +### Unreliable/Weak Connection + +Some users may experience a poor connection between the keyboard and the host. This might be due to poor quality BLE hardware, a metal enclosure on the keyboard or host, or the distance between them. Increasing the transmit power of the keyboard's BLE radio may reduce the severity of this problem. To do this, set the `CONFIG_BT_CTLR_TX_PWR_PLUS_8` configuration value in the `.conf` file of your user config directory as such: + +```ini +CONFIG_BT_CTLR_TX_PWR_PLUS_8=y +``` + +For the `nRF52840`, the value `PLUS_8` can be set to any multiple of four between `MINUS_20` and `PLUS_8`. The default value for this config is `0`, but if you are having connection issues it is recommended to set it to `PLUS_8` because the power consumption difference is negligible. For more information on changing the transmit power of your BLE device, please refer to [the Zephyr docs.](https://docs.zephyrproject.org/3.5.0/kconfig.html#CONFIG_BT_CTLR_TX_PWR) + +:::info +This setting can also improve the connection strength between the keyboard halves for split keyboards. +::: + +### Using Bluetooth Output With USB Power + +If you want to test Bluetooth output on your keyboard and are powering it through the USB connection rather than a battery, you will be able to pair with a host device but may not see keystrokes sent. In this case you need to use the [output selection behavior](../behaviors/outputs.md) to prefer sending keystrokes over Bluetooth rather than USB. This might be necessary even if you are not powering from a device capable of receiving USB inputs, such as a USB charger. + +### Issues With Dual Boot Setups + +Since ZMK associates pairing/bond keys with hardware addresses of hosts, you cannot pair to two different operating systems in a dual boot system at the same time. +While you can find [documented workarounds](https://wiki.archlinux.org/title/bluetooth#Dual_boot_pairing) that involve copying pairing keys across operating systems and use both OS with a single profile, they can be fairly involved and should be followed with caution. + +### macOS Connected but Not Working + +If you attempt to pair a ZMK keyboard from macOS in a way that causes a bonding issue, macOS may report the keyboard as connected, but fail to actually work. If this occurs: + +1. Remove the keyboard from macOS using the Bluetooth control panel. +1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the macOS device is active, by pressing the correct keys for your particular keymap. +1. Try connecting again from macOS. + +### Windows Connected but Not Working + +Occasionally pairing the keyboard to a Windows device might result in a state where the keyboard is connected but does not send any key strokes. +If this occurs: + +1. Remove the keyboard from Windows using the Bluetooth settings. +1. Invoke `&bt BT_CLR` on the keyboard while the profile associated with the Windows device is active, by pressing the correct keys for your particular keymap. +1. Turn off Bluetooth from Windows settings, then turn it back on. +1. Pair the keyboard to the Windows device. + +If this doesn't help, try following the procedure above but replace step 3 with one of the following: + +- Restart the Windows device +- Open "Device Manager," turn on "Show hidden devices" from the "View" menu, then find and delete the keyboard under the "Bluetooth" item diff --git a/docs/docs/troubleshooting/flashing-issues.md b/docs/docs/troubleshooting/flashing-issues.md new file mode 100644 index 00000000000..699555d5f92 --- /dev/null +++ b/docs/docs/troubleshooting/flashing-issues.md @@ -0,0 +1,29 @@ +--- +title: Flashing Issues +sidebar_label: Flashing Issues +description: Troubleshooting issues when flashing ZMK firmware to devices. +--- + +## File Transfer Error + +Variations of the warnings shown below occur when flashing the `.uf2` onto the microcontroller. This is because the microcontroller resets itself before the OS receives confirmation that the file transfer is complete. Errors like this are normal and can generally be ignored. Verification of a functional board can be done by attempting to pair your newly flashed keyboard to your computer via Bluetooth or plugging in a USB cable if `ZMK_USB` is enabled in your Kconfig.defconfig. + +| ![Example Error Screen](../../docs/assets/troubleshooting/filetransfer/windows.png) | +| :---------------------------------------------------------------------------------: | +| An example of the file transfer error on Windows 10 | + +| ![Example Error Screen](../../docs/assets/troubleshooting/filetransfer/linux.png) | +| :-------------------------------------------------------------------------------: | +| An example of the file transfer error on Linux | + +| ![Example Error Screen](../../docs/assets/troubleshooting/filetransfer/mac.png) | +| :-----------------------------------------------------------------------------: | +| An example of the file transfer error on macOS | + +## macOS Ventura Error + +macOS 13.0 (Ventura) Finder may report an error code 100093 when copying `.uf2` files into microcontrollers. This bug is limited to the operating system's Finder. You can work around it by copying on Terminal command line or use a third party file manager. Issue is fixed in macOS version 13.1. + +## macOS Sonoma Error + +macOS 14.x (Sonoma) Finder may report an "Error code -36" when copying `.uf2` files into microcontrollers. A similar "fcopyfile failed: Input/output error" will also be reported when copying is performed using Terminal command line. These errors can be ignored because they are reported when the bootloader disconnects automatically after the uf2 file is copied successfully. diff --git a/docs/docs/troubleshooting/index.mdx b/docs/docs/troubleshooting/index.mdx new file mode 100644 index 00000000000..45a93191b16 --- /dev/null +++ b/docs/docs/troubleshooting/index.mdx @@ -0,0 +1,10 @@ +--- +title: Troubleshooting +sidebar_label: Troubleshooting +--- + +import DocCardList from "@theme/DocCardList"; + +The following pages provide suggestions for common errors that may occur while setting up or using devices running ZMK. If the information provided is insufficient to resolve an issue, feel free to seek out additional help from the [ZMK Discord](https://zmk.dev/community/discord/invite). + + diff --git a/docs/docs/user-setup.mdx b/docs/docs/user-setup.mdx index 355fc51d5e2..f6cb5eb2717 100644 --- a/docs/docs/user-setup.mdx +++ b/docs/docs/user-setup.mdx @@ -177,9 +177,9 @@ push the initial commit. ::: -## Installing The Firmware +## Installing the Firmware -### Download The Archive +### Download the Archive Once the setup script is complete and the new user config repository has been pushed, GitHub will automatically run the action to build your keyboard firmware files. You can view the actions by clicking on the "Actions" tab on your GitHub repository. @@ -225,7 +225,7 @@ For split keyboards, after flashing each half individually you can connect them :::note If you have issues connecting the halves, make sure that both sides are getting powered properly through USB or batteries, then follow the -[recommended troubleshooting procedure](troubleshooting.md#split-keyboard-halves-unable-to-pair). This is typically necessary if you +[recommended troubleshooting procedure](troubleshooting/connection-issues.mdx#split-keyboard-halves-unable-to-pair). This is typically necessary if you swapped firmware sides between controllers, like flashing left side firmware to the same controller after flashing the right, or vice versa. ::: diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index c9a07179ea9..4017b125db7 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -31,6 +31,7 @@ module.exports = { "linker-script", "log", "powershell", + "diff", ], theme, darkTheme, @@ -79,7 +80,7 @@ module.exports = { }, { label: "Development", - to: "docs/development/setup/", + to: "docs/development/setup", }, ], }, diff --git a/docs/sidebars.js b/docs/sidebars.js index ebf0aef76e6..e8c715c8c76 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -6,7 +6,20 @@ module.exports = { "faq", "user-setup", "customization", - "troubleshooting", + { + type: "category", + label: "Troubleshooting", + link: { + type: "doc", + id: "troubleshooting/index", + }, + collapsed: true, + items: [ + "troubleshooting/building-issues", + "troubleshooting/flashing-issues", + "troubleshooting/connection-issues", + ], + }, ], Features: [ "features/keymaps", @@ -76,7 +89,16 @@ module.exports = { "development/clean-room", "development/pre-commit", "development/documentation", - "development/setup", + { + type: "category", + label: "Setup", + collapsed: true, + items: [ + "development/setup/index", + "development/setup/docker", + "development/setup/native", + ], + }, "development/build-flash", "development/boards-shields-keymaps", "development/posix-board", diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index c2df00e2c5f..99920d54c74 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -46,3 +46,7 @@ width: 100%; height: 100%; } + +.secrettabs { + display: none; +}