diff options
54 files changed, 953 insertions, 169 deletions
diff --git a/.devcontainer/.bashrc b/.devcontainer/.bashrc new file mode 100644 index 0000000..9fdb8f6 --- /dev/null +++ b/.devcontainer/.bashrc @@ -0,0 +1,8 @@ +export LS_OPTIONS='-F --color=auto' +alias ls='ls $LS_OPTIONS' +if [ "${CODESPACES}" = "true" ]; then + export WORKSPACE_DIR="$HOME/workspace/zmk" +fi +if [ -f "$WORKSPACE_DIR/zephyr/zephyr-env.sh" ]; then + source "$WORKSPACE_DIR/zephyr/zephyr-env.sh" +fi diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..184aae9 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,11 @@ +FROM zmkfirmware/zephyr-west-action-arm + +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get install --no-install-recommends -y \ + ssh \ + gpg && \ + rm -rf /var/lib/apt/lists/* + +COPY .bashrc tmp +RUN mv /tmp/.bashrc ~/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..14feeff --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,11 @@ +{ + "name": "ZMK Development", + "dockerFile": "Dockerfile", + "extensions": ["ms-vscode.cpptools"], + "runArgs": ["--security-opt", "label=disable"], + "containerEnv": {"WORKSPACE_DIR": "${containerWorkspaceFolder}"}, + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, +} + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3d05d86 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto + +# Always use Unix-style line endings for Bash scripts so they work in +# Docker on Windows. +.bashrc text eol=lf +*.sh text eol=lf diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 31d28f5..39509ed 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -46,6 +46,7 @@ if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_none.c) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) + target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c) target_sources(app PRIVATE src/keymap.c) endif() target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c) diff --git a/app/boards/shields/lily58/lily58.keymap b/app/boards/shields/lily58/lily58.keymap index 997a124..d44b3fe 100644 --- a/app/boards/shields/lily58/lily58.keymap +++ b/app/boards/shields/lily58/lily58.keymap @@ -7,6 +7,7 @@ #include <behaviors.dtsi> #include <dt-bindings/zmk/keys.h> #include <dt-bindings/zmk/bt.h> +#include <dt-bindings/zmk/ext_power.h> / { keymap { @@ -38,11 +39,11 @@ // | | | | | | | | | | | _ | + | { | } | "|" | // | | | | | | | | | | bindings = < -&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &trans &trans &trans &trans &trans &trans -&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 -&kp GRAV &kp BANG &kp ATSN &kp HASH &kp CURU &kp PRCT &kp CRRT &kp AMPS &kp KMLT &kp LPRN &kp RPRN &kp TILD -&trans &trans &trans &trans &trans &trans &trans &trans &trans &kp MINUS &kp KPLS &kp LCUR &kp RCUR &kp PIPE - &trans &trans &trans &trans &trans &trans &trans &trans +&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &trans &trans &trans &trans &trans &trans +&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 +&kp GRAV &kp BANG &kp ATSN &kp HASH &kp CURU &kp PRCT &kp CRRT &kp AMPS &kp KMLT &kp LPRN &kp RPRN &kp TILD +&trans &ext_power EP_ON &ext_power EP_OFF &ext_power EP_TOG &trans &trans &trans &trans &trans &kp MINUS &kp KPLS &kp LCUR &kp RCUR &kp PIPE + &trans &trans &trans &trans &trans &trans &trans &trans >; sensor-bindings = <&inc_dec_cp M_VOLU M_VOLD>; diff --git a/app/boards/shields/qaz/qaz.conf b/app/boards/shields/qaz/qaz.conf new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/boards/shields/qaz/qaz.conf diff --git a/app/boards/shields/tg4x/Kconfig.defconfig b/app/boards/shields/tg4x/Kconfig.defconfig new file mode 100644 index 0000000..ca9fa2c --- /dev/null +++ b/app/boards/shields/tg4x/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_TG4X + +config ZMK_KEYBOARD_NAME + default "TG4X" + +endif
\ No newline at end of file diff --git a/app/boards/shields/tg4x/Kconfig.shield b/app/boards/shields/tg4x/Kconfig.shield new file mode 100644 index 0000000..7e98b71 --- /dev/null +++ b/app/boards/shields/tg4x/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_TG4X + def_bool $(shields_list_contains,tg4x) diff --git a/app/boards/shields/tg4x/tg4x.keymap b/app/boards/shields/tg4x/tg4x.keymap new file mode 100644 index 0000000..bee5c87 --- /dev/null +++ b/app/boards/shields/tg4x/tg4x.keymap @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include <behaviors.dtsi> +#include <dt-bindings/zmk/keys.h> +#include <dt-bindings/zmk/bt.h> + +#define DEFAULT 0 +#define LOWER 1 +#define RAISE 2 + +/ { + behaviors { + hm: homerow_mods { + compatible = "zmk,behavior-hold-tap"; + label = "homerow mods"; + #binding-cells = <2>; + tapping_term_ms = <225>; + flavor = "tap-preferred"; + bindings = <&kp>, <&kp>; + }; + }; +}; + +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BKSP + &kp TAB &hm LGUI A &hm LALT S &hm LCTL D &hm LSFT F &kp G &kp H &hm RSFT J &hm RCTL K &hm RALT L &hm RGUI SCLN &kp RET + &kp LSFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp CMMA &kp DOT &kp FSLH &kp QUOT + &kp LCTL &kp LALT &kp LGUI < 1 BKSP < 2 SPC &kp LARW &kp DARW &kp UARW &kp RARW + >; + }; + lower { + bindings = < + &kp GRAV &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp PRSC + &kp DEL &trans &kp VOLU &trans &trans &trans &trans &kp LARW &kp DARW &kp UARW &kp RARW &trans + &trans &trans &kp VOLD &trans &trans &trans &trans &trans &trans &bt BT_PRV &bt BT_NXT &bt BT_CLR + &bootloader &reset &trans &trans &trans &trans &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 + >; + }; + + raise { + bindings = < + &kp GRAV &kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &kp NUM_6 &kp NUM_7 &kp NUM_8 &kp NUM_9 &kp NUM_0 &kp PRSC + &kp DEL &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp MINUS &kp EQL &kp LBKT &kp RBKT &kp BSLH + &trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp TILD &kp HOME &kp PGUP &kp PGDN &kp END + &trans &trans &trans &trans &trans &trans &kp M_NEXT &kp M_VOLD &kp M_VOLU &kp M_PLAY + >; + }; + }; +};
\ No newline at end of file diff --git a/app/boards/shields/tg4x/tg4x.overlay b/app/boards/shields/tg4x/tg4x.overlay new file mode 100644 index 0000000..10ce524 --- /dev/null +++ b/app/boards/shields/tg4x/tg4x.overlay @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 The ZMK Contrbutors + * + * SPDX-License-Identifier: MIT + */ + +#include <dt-bindings/zmk/matrix-transform.h> + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <7>; + rows = <8>; + map = < + RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,5) + RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) + RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4) + RC(3,0) RC(3,1) RC(3,2) RC(3,4) RC(3,5) RC(7,1) RC(7,2) RC(7,3) RC(7,4) + >; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + diode-direction = "col2row"; + + col-gpios + = <&pro_micro_d 1 GPIO_ACTIVE_HIGH> + , <&pro_micro_d 14 GPIO_ACTIVE_HIGH> + , <&pro_micro_d 15 GPIO_ACTIVE_HIGH> + , <&pro_micro_a 0 GPIO_ACTIVE_HIGH> + , <&pro_micro_a 1 GPIO_ACTIVE_HIGH> + , <&pro_micro_a 2 GPIO_ACTIVE_HIGH> + , <&pro_micro_a 3 GPIO_ACTIVE_HIGH> + ; + + row-gpios + = <&pro_micro_a 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_a 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_d 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_a 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_d 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_a 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_d 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro_d 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + + }; + +}; + diff --git a/app/drivers/zephyr/kscan_gpio_matrix.c b/app/drivers/zephyr/kscan_gpio_matrix.c index a0b22e4..ec4fb39 100644 --- a/app/drivers/zephyr/kscan_gpio_matrix.c +++ b/app/drivers/zephyr/kscan_gpio_matrix.c @@ -181,19 +181,18 @@ static int kscan_gpio_config_interrupts(struct device **devices, struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \ kscan_gpio_read_##n(data->dev); \ } \ - COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \ - (static void kscan_gpio_irq_callback_handler_##n( \ - struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pin) { \ - struct kscan_gpio_irq_callback_##n *data = \ - CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \ - kscan_gpio_disable_interrupts_##n(data->dev); \ - COND_CODE_0(DT_INST_PROP(n, debounce_period), \ - ({ k_work_submit(data->work); }), ({ \ - k_delayed_work_cancel(data->work); \ - k_delayed_work_submit( \ - data->work, K_MSEC(DT_INST_PROP(n, debounce_period))); \ - })) \ - })) \ + static void kscan_gpio_irq_callback_handler_##n(struct device *dev, struct gpio_callback *cb, \ + gpio_port_pins_t pin) { \ + struct kscan_gpio_irq_callback_##n *data = \ + CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \ + COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \ + (kscan_gpio_disable_interrupts_##n(data->dev);)) \ + COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \ + k_delayed_work_cancel(data->work); \ + k_delayed_work_submit(data->work, \ + K_MSEC(DT_INST_PROP(n, debounce_period))); \ + })) \ + } \ \ static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \ .rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \ diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 202202b..36c918c 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -8,4 +8,5 @@ #include <behaviors/reset.dtsi> #include <behaviors/sensor_rotate_key_press.dtsi> #include <behaviors/rgb_underglow.dtsi> -#include <behaviors/bluetooth.dtsi>
\ No newline at end of file +#include <behaviors/bluetooth.dtsi> +#include <behaviors/ext_power.dtsi> diff --git a/app/dts/behaviors/ext_power.dtsi b/app/dts/behaviors/ext_power.dtsi new file mode 100644 index 0000000..92f0035 --- /dev/null +++ b/app/dts/behaviors/ext_power.dtsi @@ -0,0 +1,9 @@ +/ { + behaviors { + ext_power: behavior_ext_power { + compatible = "zmk,behavior-ext-power"; + label = "EXT_POWER_BEHAVIOR"; + #binding-cells = <1>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-ext-power.yaml b/app/dts/bindings/behaviors/zmk,behavior-ext-power.yaml new file mode 100644 index 0000000..d86c6f9 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-ext-power.yaml @@ -0,0 +1,10 @@ +# +# Copyright (c) 2020, The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +description: External power control Behavior + +compatible: "zmk,behavior-ext-power" + +include: one_param.yaml diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h index 45b8bea..cf259b1 100644 --- a/app/include/drivers/behavior.h +++ b/app/include/drivers/behavior.h @@ -10,6 +10,7 @@ #include <stddef.h> #include <device.h> #include <zmk/keys.h> +#include <zmk/behavior.h> /** * @cond INTERNAL_HIDDEN @@ -19,10 +20,10 @@ * (Internal use only.) */ -typedef int (*behavior_keymap_binding_callback_t)(struct device *dev, u32_t position, u32_t param1, - u32_t param2); -typedef int (*behavior_sensor_keymap_binding_callback_t)(struct device *dev, struct device *sensor, - u32_t param1, u32_t param2); +typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event); +typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_binding *binding, + struct device *sensor); __subsystem struct behavior_driver_api { behavior_keymap_binding_callback_t binding_pressed; @@ -42,18 +43,19 @@ __subsystem struct behavior_driver_api { * @retval 0 If successful. * @retval Negative errno code if failure. */ -__syscall int behavior_keymap_binding_pressed(struct device *dev, u32_t position, u32_t param1, - u32_t param2); +__syscall int behavior_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event); -static inline int z_impl_behavior_keymap_binding_pressed(struct device *dev, u32_t position, - u32_t param1, u32_t param2) { +static inline int z_impl_behavior_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->driver_api; if (api->binding_pressed == NULL) { return -ENOTSUP; } - return api->binding_pressed(dev, position, param1, param2); + return api->binding_pressed(binding, event); } /** @@ -64,18 +66,19 @@ static inline int z_impl_behavior_keymap_binding_pressed(struct device *dev, u32 * @retval 0 If successful. * @retval Negative errno code if failure. */ -__syscall int behavior_keymap_binding_released(struct device *dev, u32_t position, u32_t param1, - u32_t param2); +__syscall int behavior_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event); -static inline int z_impl_behavior_keymap_binding_released(struct device *dev, u32_t position, - u32_t param1, u32_t param2) { +static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->driver_api; if (api->binding_released == NULL) { return -ENOTSUP; } - return api->binding_released(dev, position, param1, param2); + return api->binding_released(binding, event); } /** @@ -88,19 +91,20 @@ static inline int z_impl_behavior_keymap_binding_released(struct device *dev, u3 * @retval 0 If successful. * @retval Negative errno code if failure. */ -__syscall int behavior_sensor_keymap_binding_triggered(struct device *dev, struct device *sensor, - u32_t param1, u32_t param2); +__syscall int behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding, + struct device *sensor); -static inline int z_impl_behavior_sensor_keymap_binding_triggered(struct device *dev, - struct device *sensor, - u32_t param1, u32_t param2) { +static inline int +z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding, + struct device *sensor) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->driver_api; if (api->sensor_binding_triggered == NULL) { return -ENOTSUP; } - return api->sensor_binding_triggered(dev, sensor, param1, param2); + return api->sensor_binding_triggered(binding, sensor); } /** diff --git a/app/include/dt-bindings/zmk/bt.h b/app/include/dt-bindings/zmk/bt.h index a403d35..8ca1060 100644 --- a/app/include/dt-bindings/zmk/bt.h +++ b/app/include/dt-bindings/zmk/bt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> + * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ diff --git a/app/include/dt-bindings/zmk/ext_power.h b/app/include/dt-bindings/zmk/ext_power.h new file mode 100644 index 0000000..2a3e846 --- /dev/null +++ b/app/include/dt-bindings/zmk/ext_power.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define EXT_POWER_OFF_CMD 0 +#define EXT_POWER_ON_CMD 1 +#define EXT_POWER_TOGGLE_CMD 2 + +#define EP_ON EXT_POWER_ON_CMD +#define EP_OFF EXT_POWER_OFF_CMD +#define EP_TOG EXT_POWER_TOGGLE_CMD diff --git a/app/include/zmk/behavior.h b/app/include/zmk/behavior.h index 6f5815f..428ae24 100644 --- a/app/include/zmk/behavior.h +++ b/app/include/zmk/behavior.h @@ -10,4 +10,10 @@ struct zmk_behavior_binding { char *behavior_dev; u32_t param1; u32_t param2; +}; + +struct zmk_behavior_binding_event { + int layer; + u32_t position; + s64_t timestamp; };
\ No newline at end of file diff --git a/app/include/zmk/ble/profile.h b/app/include/zmk/ble/profile.h index 9a79c6d..1df2743 100644 --- a/app/include/zmk/ble/profile.h +++ b/app/include/zmk/ble/profile.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> + * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ diff --git a/app/include/zmk/events/ble-active-profile-changed.h b/app/include/zmk/events/ble-active-profile-changed.h index 66f40c7..1e3a198 100644 --- a/app/include/zmk/events/ble-active-profile-changed.h +++ b/app/include/zmk/events/ble-active-profile-changed.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> + * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ diff --git a/app/include/zmk/events/keycode-state-changed.h b/app/include/zmk/events/keycode-state-changed.h index 4c00654..1e2c24e 100644 --- a/app/include/zmk/events/keycode-state-changed.h +++ b/app/include/zmk/events/keycode-state-changed.h @@ -24,6 +24,5 @@ inline struct keycode_state_changed *create_keycode_state_changed(u8_t usage_pag ev->usage_page = usage_page; ev->keycode = keycode; ev->state = state; - return ev; }
\ No newline at end of file diff --git a/app/include/zmk/events/position-state-changed.h b/app/include/zmk/events/position-state-changed.h index f88080d..e4cbbbe 100644 --- a/app/include/zmk/events/position-state-changed.h +++ b/app/include/zmk/events/position-state-changed.h @@ -13,6 +13,7 @@ struct position_state_changed { struct zmk_event_header header; u32_t position; bool state; + s64_t timestamp; }; ZMK_EVENT_DECLARE(position_state_changed);
\ No newline at end of file diff --git a/app/include/zmk/keymap.h b/app/include/zmk/keymap.h index 6192587..b8f4969 100644 --- a/app/include/zmk/keymap.h +++ b/app/include/zmk/keymap.h @@ -11,4 +11,4 @@ int zmk_keymap_layer_activate(u8_t layer); int zmk_keymap_layer_deactivate(u8_t layer); int zmk_keymap_layer_toggle(u8_t layer); -int zmk_keymap_position_state_changed(u32_t position, bool pressed); +int zmk_keymap_position_state_changed(u32_t position, bool pressed, s64_t timestamp); diff --git a/app/src/behaviors/behavior_bt.c b/app/src/behaviors/behavior_bt.c index 09fadba..066c437 100644 --- a/app/src/behaviors/behavior_bt.c +++ b/app/src/behaviors/behavior_bt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> + * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ @@ -8,18 +8,18 @@ #include <device.h> #include <drivers/behavior.h> - #include <dt-bindings/zmk/bt.h> - #include <bluetooth/conn.h> - #include <logging/log.h> +#include <zmk/behavior.h> + LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include <zmk/ble.h> -static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t command, u32_t arg) { - switch (command) { +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + switch (binding->param1) { case BT_CLR_CMD: return zmk_ble_clear_bonds(); case BT_NXT_CMD: @@ -27,9 +27,9 @@ static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t c case BT_PRV_CMD: return zmk_ble_prof_prev(); case BT_SEL_CMD: - return zmk_ble_prof_select(arg); + return zmk_ble_prof_select(binding->param2); default: - LOG_ERR("Unknown BT command: %d", command); + LOG_ERR("Unknown BT command: %d", binding->param1); } return -ENOTSUP; @@ -37,8 +37,8 @@ static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t c static int behavior_bt_init(struct device *dev) { return 0; }; -static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t command, - u32_t arg) { +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { return 0; } diff --git a/app/src/behaviors/behavior_ext_power.c b/app/src/behaviors/behavior_ext_power.c new file mode 100644 index 0000000..825f983 --- /dev/null +++ b/app/src/behaviors/behavior_ext_power.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_ext_power + +#include <device.h> +#include <devicetree.h> +#include <drivers/behavior.h> +#include <drivers/ext_power.h> + +#include <dt-bindings/zmk/ext_power.h> + +#include <logging/log.h> +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *ext_power = device_get_binding("EXT_POWER"); + if (ext_power == NULL) { + LOG_ERR("Unable to retrieve ext_power device: %d", binding->param1); + return -EIO; + } + + switch (binding->param1) { + case EXT_POWER_OFF_CMD: + return ext_power_disable(ext_power); + case EXT_POWER_ON_CMD: + return ext_power_enable(ext_power); + case EXT_POWER_TOGGLE_CMD: + if (ext_power_get(ext_power) > 0) + return ext_power_disable(ext_power); + else + return ext_power_enable(ext_power); + default: + LOG_ERR("Unknown ext_power command: %d", binding->param1); + } + + return -ENOTSUP; +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static int behavior_ext_power_init(struct device *dev) { return 0; }; + +static const struct behavior_driver_api behavior_ext_power_driver_api = { + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_binding_released, +}; + +DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL, + APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api); diff --git a/app/src/behaviors/behavior_hold_tap.c b/app/src/behaviors/behavior_hold_tap.c index 8f307a6..8b3620e 100644 --- a/app/src/behaviors/behavior_hold_tap.c +++ b/app/src/behaviors/behavior_hold_tap.c @@ -10,7 +10,6 @@ #include <drivers/behavior.h> #include <logging/log.h> #include <zmk/behavior.h> - #include <zmk/matrix.h> #include <zmk/endpoints.h> #include <zmk/event-manager.h> @@ -18,6 +17,7 @@ #include <zmk/events/keycode-state-changed.h> #include <zmk/events/modifiers-state-changed.h> #include <zmk/hid.h> +#include <zmk/behavior.h> LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -40,10 +40,8 @@ struct behavior_hold_tap_behaviors { struct zmk_behavior_binding hold; }; -typedef k_timeout_t (*timer_func)(); - struct behavior_hold_tap_config { - timer_func tapping_term_ms; + int tapping_term_ms; struct behavior_hold_tap_behaviors *behaviors; enum flavor flavor; }; @@ -51,8 +49,10 @@ struct behavior_hold_tap_config { // this data is specific for each hold-tap struct active_hold_tap { s32_t position; + // todo: move these params into the config->behaviors->tap and u32_t param_hold; u32_t param_tap; + s64_t timestamp; bool is_decided; bool is_hold; const struct behavior_hold_tap_config *config; @@ -164,6 +164,7 @@ static struct active_hold_tap *find_hold_tap(u32_t position) { } static struct active_hold_tap *store_hold_tap(u32_t position, u32_t param_hold, u32_t param_tap, + s64_t timestamp, const struct behavior_hold_tap_config *config) { for (int i = 0; i < ZMK_BHV_HOLD_TAP_MAX_HELD; i++) { if (active_hold_taps[i].position != ZMK_BHV_HOLD_TAP_POSITION_NOT_USED) { @@ -175,6 +176,7 @@ static struct active_hold_tap *store_hold_tap(u32_t position, u32_t param_hold, active_hold_taps[i].config = config; active_hold_taps[i].param_hold = param_hold; active_hold_taps[i].param_tap = param_tap; + active_hold_taps[i].timestamp = timestamp; return &active_hold_taps[i]; } return NULL; @@ -253,7 +255,7 @@ static inline char *flavor_str(enum flavor flavor) { return "UNKNOWN FLAVOR"; } -static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment event) { +static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_moment event_type) { if (hold_tap->is_decided) { return; } @@ -265,11 +267,11 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_mome switch (hold_tap->config->flavor) { case ZMK_BHV_HOLD_TAP_FLAVOR_HOLD_PREFERRED: - decide_hold_preferred(hold_tap, event); + decide_hold_preferred(hold_tap, event_type); case ZMK_BHV_HOLD_TAP_FLAVOR_BALANCED: - decide_balanced(hold_tap, event); + decide_balanced(hold_tap, event_type); case ZMK_BHV_HOLD_TAP_FLAVOR_TAP_PREFERRED: - decide_tap_preferred(hold_tap, event); + decide_tap_preferred(hold_tap, event_type); } if (!hold_tap->is_decided) { @@ -277,26 +279,31 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap, enum decision_mome } LOG_DBG("%d decided %s (%s event %d)", hold_tap->position, hold_tap->is_hold ? "hold" : "tap", - flavor_str(hold_tap->config->flavor), event); + flavor_str(hold_tap->config->flavor), event_type); undecided_hold_tap = NULL; - struct zmk_behavior_binding *behavior; + struct zmk_behavior_binding_event event = { + .position = hold_tap->position, + .timestamp = hold_tap->timestamp, + }; + + struct zmk_behavior_binding binding; if (hold_tap->is_hold) { - behavior = &hold_tap->config->behaviors->hold; - struct device *behavior_device = device_get_binding(behavior->behavior_dev); - behavior_keymap_binding_pressed(behavior_device, hold_tap->position, hold_tap->param_hold, - 0); + binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev; + binding.param1 = hold_tap->param_hold; + binding.param2 = 0; } else { - behavior = &hold_tap->config->behaviors->tap; - struct device *behavior_device = device_get_binding(behavior->behavior_dev); - behavior_keymap_binding_pressed(behavior_device, hold_tap->position, hold_tap->param_tap, - 0); + binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev; + binding.param1 = hold_tap->param_tap; + binding.param2 = 0; } + behavior_keymap_binding_pressed(&binding, event); release_captured_events(); } -static int on_hold_tap_binding_pressed(struct device *dev, u32_t position, u32_t param_hold, - u32_t param_tap) { +static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_hold_tap_config *cfg = dev->config_info; if (undecided_hold_tap != NULL) { @@ -305,54 +312,69 @@ static int on_hold_tap_binding_pressed(struct device *dev, u32_t position, u32_t return 0; } - struct active_hold_tap *hold_tap = store_hold_tap(position, param_hold, param_tap, cfg); + struct active_hold_tap *hold_tap = + store_hold_tap(event.position, binding->param1, binding->param2, event.timestamp, cfg); if (hold_tap == NULL) { LOG_ERR("unable to store hold-tap info, did you press more than %d hold-taps?", ZMK_BHV_HOLD_TAP_MAX_HELD); return 0; } - LOG_DBG("%d new undecided hold_tap", position); + LOG_DBG("%d new undecided hold_tap", event.position); undecided_hold_tap = hold_tap; - k_delayed_work_submit(&hold_tap->work, cfg->tapping_term_ms()); - // todo: once we get timing info for keypresses, start the timer relative to the original - // keypress don't forget to simulate a timer-event before the event after that time was handled. + // if this behavior was queued we have to adjust the timer to only + // wait for the remaining time. + s32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get(); + if (tapping_term_ms_left > 0) { + k_delayed_work_submit(&hold_tap->work, K_MSEC(tapping_term_ms_left)); + } return 0; } -static int on_hold_tap_binding_released(struct device *dev, u32_t position, u32_t _, u32_t __) { - struct active_hold_tap *hold_tap = find_hold_tap(position); - +static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct active_hold_tap *hold_tap = find_hold_tap(event.position); if (hold_tap == NULL) { LOG_ERR("ACTIVE_HOLD_TAP_CLEANED_UP_TOO_EARLY"); return 0; } + // If these events were queued, the timer event may be queued too late or not at all. + // We insert a timer event before the TH_KEY_UP event to verify. int work_cancel_result = k_delayed_work_cancel(&hold_tap->work); + if (event.timestamp > (hold_tap->timestamp + hold_tap->config->tapping_term_ms)) { + decide_hold_tap(hold_tap, HT_TIMER_EVENT); + } + decide_hold_tap(hold_tap, HT_KEY_UP); - struct zmk_behavior_binding *behavior; + // todo: set up the binding and data items inside of the active_hold_tap struct + struct zmk_behavior_binding_event sub_behavior_data = { + .position = hold_tap->position, + .timestamp = hold_tap->timestamp, + }; + + struct zmk_behavior_binding sub_behavior_binding; if (hold_tap->is_hold) { - behavior = &hold_tap->config->behaviors->hold; - struct device *behavior_device = device_get_binding(behavior->behavior_dev); - behavior_keymap_binding_released(behavior_device, hold_tap->position, hold_tap->param_hold, - 0); + sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->hold.behavior_dev; + sub_behavior_binding.param1 = hold_tap->param_hold; + sub_behavior_binding.param2 = 0; } else { - behavior = &hold_tap->config->behaviors->tap; - struct device *behavior_device = device_get_binding(behavior->behavior_dev); - behavior_keymap_binding_released(behavior_device, hold_tap->position, hold_tap->param_tap, - 0); + sub_behavior_binding.behavior_dev = hold_tap->config->behaviors->tap.behavior_dev; + sub_behavior_binding.param1 = hold_tap->param_tap; + sub_behavior_binding.param2 = 0; } + behavior_keymap_binding_released(&sub_behavior_binding, sub_behavior_data); if (work_cancel_result == -EINPROGRESS) { // let the timer handler clean up // if we'd clear now, the timer may call back for an uninitialized active_hold_tap. - LOG_DBG("%d hold-tap timer work in event queue", position); + LOG_DBG("%d hold-tap timer work in event queue", event.position); hold_tap->work_is_cancelled = true; } else { - LOG_DBG("%d cleaning up hold-tap", position); + LOG_DBG("%d cleaning up hold-tap", event.position); clear_hold_tap(hold_tap); } @@ -382,6 +404,14 @@ static int position_state_changed_listener(const struct zmk_event_header *eh) { } } + // If these events were queued, the timer event may be queued too late or not at all. + // We make a timer decision before the other key events are handled if the timer would + // have run out. + if (ev->timestamp > + (undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) { + decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT); + } + if (!ev->state && find_captured_keydown_event(ev->position) == NULL) { // no keydown event has been captured, let it bubble. // we'll catch modifiers later in modifier_state_changed_listener @@ -463,6 +493,7 @@ static int behavior_hold_tap_init(struct device *dev) { struct behavior_hold_tap_data {}; static struct behavior_hold_tap_data behavior_hold_tap_data; +/* todo: get rid of unused param1 and param2. */ #define _TRANSFORM_ENTRY(idx, node) \ { \ .behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \ @@ -473,14 +504,11 @@ static struct behavior_hold_tap_data behavior_hold_tap_data; }, #define KP_INST(n) \ - static k_timeout_t behavior_hold_tap_config_##n##_gettime() { \ - return K_MSEC(DT_INST_PROP(n, tapping_term_ms)); \ - } \ static struct behavior_hold_tap_behaviors behavior_hold_tap_behaviors_##n = { \ .hold = _TRANSFORM_ENTRY(0, n).tap = _TRANSFORM_ENTRY(1, n)}; \ static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \ .behaviors = &behavior_hold_tap_behaviors_##n, \ - .tapping_term_ms = &behavior_hold_tap_config_##n##_gettime, \ + .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \ .flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \ }; \ DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \ diff --git a/app/src/behaviors/behavior_key_press.c b/app/src/behaviors/behavior_key_press.c index bbfbe36..923b098 100644 --- a/app/src/behaviors/behavior_key_press.c +++ b/app/src/behaviors/behavior_key_press.c @@ -12,6 +12,7 @@ #include <zmk/event-manager.h> #include <zmk/events/keycode-state-changed.h> +#include <zmk/behavior.h> LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -22,18 +23,24 @@ struct behavior_key_press_data {}; static int behavior_key_press_init(struct device *dev) { return 0; }; -static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t keycode, u32_t _) { +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_key_press_config *cfg = dev->config_info; - LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", position, cfg->usage_page, keycode); + LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", event.position, cfg->usage_page, + binding->param1); - return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, keycode, true)); + return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, binding->param1, true)); } -static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t keycode, u32_t _) { +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_key_press_config *cfg = dev->config_info; - LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", position, cfg->usage_page, keycode); + LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", event.position, cfg->usage_page, + binding->param1); - return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, keycode, false)); + return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, binding->param1, false)); } static const struct behavior_driver_api behavior_key_press_driver_api = { @@ -47,4 +54,4 @@ static const struct behavior_driver_api behavior_key_press_driver_api = { &behavior_key_press_data_##n, &behavior_key_press_config_##n, APPLICATION, \ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_press_driver_api); -DT_INST_FOREACH_STATUS_OKAY(KP_INST)
\ No newline at end of file +DT_INST_FOREACH_STATUS_OKAY(KP_INST) diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index 80b7165..b1fb14b 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -11,6 +11,7 @@ #include <logging/log.h> #include <zmk/keymap.h> +#include <zmk/behavior.h> LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -19,16 +20,16 @@ struct behavior_mo_data {}; static int behavior_mo_init(struct device *dev) { return 0; }; -static int mo_keymap_binding_pressed(struct device *dev, u32_t position, u32_t layer, u32_t _) { - LOG_DBG("position %d layer %d", position, layer); - - return zmk_keymap_layer_activate(layer); +static int mo_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d layer %d", event.position, binding->param1); + return zmk_keymap_layer_activate(binding->param1); } -static int mo_keymap_binding_released(struct device *dev, u32_t position, u32_t layer, u32_t _) { - LOG_DBG("position %d layer %d", position, layer); - - return zmk_keymap_layer_deactivate(layer); +static int mo_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d layer %d", event.position, binding->param1); + return zmk_keymap_layer_deactivate(binding->param1); } static const struct behavior_driver_api behavior_mo_driver_api = { diff --git a/app/src/behaviors/behavior_none.c b/app/src/behaviors/behavior_none.c index b548e6f..96ea9d5 100644 --- a/app/src/behaviors/behavior_none.c +++ b/app/src/behaviors/behavior_none.c @@ -11,6 +11,8 @@ #include <drivers/behavior.h> #include <logging/log.h> +#include <zmk/behavior.h> + LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct behavior_none_config {}; @@ -18,13 +20,13 @@ struct behavior_none_data {}; static int behavior_none_init(struct device *dev) { return 0; }; -static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t _param1, - u32_t _param2) { +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { return 0; } -static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t _param1, - u32_t _param2) { +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { return 0; } diff --git a/app/src/behaviors/behavior_reset.c b/app/src/behaviors/behavior_reset.c index 90de20b..d1233a5 100644 --- a/app/src/behaviors/behavior_reset.c +++ b/app/src/behaviors/behavior_reset.c @@ -11,6 +11,8 @@ #include <drivers/behavior.h> #include <logging/log.h> +#include <zmk/behavior.h> + LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct behavior_reset_config { @@ -19,8 +21,9 @@ struct behavior_reset_config { static int behavior_reset_init(struct device *dev) { return 0; }; -static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t _param1, - u32_t _param2) { +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_reset_config *cfg = dev->config_info; // TODO: Correct magic code for going into DFU? diff --git a/app/src/behaviors/behavior_rgb_underglow.c b/app/src/behaviors/behavior_rgb_underglow.c index 621eab5..2ee6716 100644 --- a/app/src/behaviors/behavior_rgb_underglow.c +++ b/app/src/behaviors/behavior_rgb_underglow.c @@ -12,13 +12,15 @@ #include <dt-bindings/zmk/rgb.h> #include <zmk/rgb_underglow.h> +#include <zmk/keymap.h> LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static int behavior_rgb_underglow_init(struct device *dev) { return 0; } -static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t action, u32_t _) { - switch (action) { +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + switch (binding->param1) { case RGB_TOG: return zmk_rgb_underglow_toggle(); case RGB_HUI: diff --git a/app/src/behaviors/behavior_sensor_rotate_key_press.c b/app/src/behaviors/behavior_sensor_rotate_key_press.c index 1a0bf03..71c4376 100644 --- a/app/src/behaviors/behavior_sensor_rotate_key_press.c +++ b/app/src/behaviors/behavior_sensor_rotate_key_press.c @@ -23,15 +23,16 @@ struct behavior_sensor_rotate_key_press_data {}; static int behavior_sensor_rotate_key_press_init(struct device *dev) { return 0; }; -static int on_sensor_binding_triggered(struct device *dev, struct device *sensor, - u32_t increment_keycode, u32_t decrement_keycode) { +static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding, + struct device *sensor) { + struct device *dev = device_get_binding(binding->behavior_dev); const struct behavior_sensor_rotate_key_press_config *cfg = dev->config_info; struct sensor_value value; int err; u32_t keycode; struct keycode_state_changed *ev; LOG_DBG("usage_page 0x%02X inc keycode 0x%02X dec keycode 0x%02X", cfg->usage_page, - increment_keycode, decrement_keycode); + binding->param1, binding->param2); err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value); @@ -42,10 +43,10 @@ static int on_sensor_binding_triggered(struct device *dev, struct device *sensor switch (value.val1) { case 1: - keycode = increment_keycode; + keycode = binding->param1; break; case -1: - keycode = decrement_keycode; + keycode = binding->param2; break; default: return -ENOTSUP; diff --git a/app/src/behaviors/behavior_toggle_layer.c b/app/src/behaviors/behavior_toggle_layer.c index 2819451..b3c6961 100644 --- a/app/src/behaviors/behavior_toggle_layer.c +++ b/app/src/behaviors/behavior_toggle_layer.c @@ -11,6 +11,7 @@ #include <logging/log.h> #include <zmk/keymap.h> +#include <zmk/behavior.h> LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -19,15 +20,15 @@ struct behavior_tog_data {}; static int behavior_tog_init(struct device *dev) { return 0; }; -static int tog_keymap_binding_pressed(struct device *dev, u32_t position, u32_t layer, u32_t _) { - LOG_DBG("position %d layer %d", position, layer); - - return zmk_keymap_layer_toggle(layer); +static int tog_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d layer %d", event.position, binding->param1); + return zmk_keymap_layer_toggle(binding->param1); } -static int tog_keymap_binding_released(struct device *dev, u32_t position, u32_t layer, u32_t _) { - LOG_DBG("position %d layer %d", position, layer); - +static int tog_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d layer %d", event.position, binding->param1); return 0; } diff --git a/app/src/behaviors/behavior_transparent.c b/app/src/behaviors/behavior_transparent.c index f7852f3..cede369 100644 --- a/app/src/behaviors/behavior_transparent.c +++ b/app/src/behaviors/behavior_transparent.c @@ -11,6 +11,8 @@ #include <drivers/behavior.h> #include <logging/log.h> +#include <zmk/behavior.h> + LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct behavior_transparent_config {}; @@ -18,13 +20,13 @@ struct behavior_transparent_data {}; static int behavior_transparent_init(struct device *dev) { return 0; }; -static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t _param1, - u32_t _param2) { +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { return 1; } -static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t _param1, - u32_t _param2) { +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { return 1; } diff --git a/app/src/events/ble_active_profile_changed.c b/app/src/events/ble_active_profile_changed.c index a270a14..06988e2 100644 --- a/app/src/events/ble_active_profile_changed.c +++ b/app/src/events/ble_active_profile_changed.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> + * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ diff --git a/app/src/hog.c b/app/src/hog.c index 11349ac..bcd652d 100644 --- a/app/src/hog.c +++ b/app/src/hog.c @@ -164,8 +164,10 @@ int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report) { LOG_DBG("Sending to NULL? %s", conn == NULL ? "yes" : "no"); - return bt_gatt_notify(conn, &hog_svc.attrs[5], report, - sizeof(struct zmk_hid_keypad_report_body)); + int err = + bt_gatt_notify(conn, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body)); + bt_conn_unref(conn); + return err; }; int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) { @@ -174,6 +176,8 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) { return -ENOTCONN; } - return bt_gatt_notify(conn, &hog_svc.attrs[10], report, - sizeof(struct zmk_hid_consumer_report_body)); + int err = bt_gatt_notify(conn, &hog_svc.attrs[10], report, + sizeof(struct zmk_hid_consumer_report_body)); + bt_conn_unref(conn); + return err; }; diff --git a/app/src/keymap.c b/app/src/keymap.c index a87ce04..74fe60d 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -104,9 +104,14 @@ bool is_active_layer(u8_t layer, u32_t layer_state) { return (layer_state & BIT(layer)) == BIT(layer) || layer == zmk_keymap_layer_default; } -int zmk_keymap_apply_position_state(int layer, u32_t position, bool pressed) { +int zmk_keymap_apply_position_state(int layer, u32_t position, bool pressed, s64_t timestamp) { struct zmk_behavior_binding *binding = &zmk_keymap[layer][position]; struct device *behavior; + struct zmk_behavior_binding_event event = { + .layer = layer, + .position = position, + .timestamp = timestamp, + }; LOG_DBG("layer: %d position: %d, binding name: %s", layer, position, log_strdup(binding->behavior_dev)); @@ -119,20 +124,18 @@ int zmk_keymap_apply_position_state(int layer, u32_t position, bool pressed) { } if (pressed) { - return behavior_keymap_binding_pressed(behavior, position, binding->param1, - binding->param2); + return behavior_keymap_binding_pressed(binding, event); } else { - return behavior_keymap_binding_released(behavior, position, binding->param1, - binding->param2); + return behavior_keymap_binding_released(binding, event); } } -int zmk_keymap_position_state_changed(u32_t position, bool pressed) { +int zmk_keymap_position_state_changed(u32_t position, bool pressed, s64_t timestamp) { for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= zmk_keymap_layer_default; layer--) { u32_t layer_state = pressed ? zmk_keymap_layer_state : zmk_keymap_active_behavior_layer[position]; if (is_active_layer(layer, layer_state)) { - int ret = zmk_keymap_apply_position_state(layer, position, pressed); + int ret = zmk_keymap_apply_position_state(layer, position, pressed, timestamp); zmk_keymap_active_behavior_layer[position] = zmk_keymap_layer_state; @@ -171,8 +174,7 @@ int zmk_keymap_sensor_triggered(u8_t sensor_number, struct device *sensor) { continue; } - ret = behavior_sensor_keymap_binding_triggered(behavior, sensor, binding->param1, - binding->param2); + ret = behavior_sensor_keymap_binding_triggered(binding, sensor); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); @@ -194,7 +196,7 @@ int zmk_keymap_sensor_triggered(u8_t sensor_number, struct device *sensor) { int keymap_listener(const struct zmk_event_header *eh) { if (is_position_state_changed(eh)) { const struct position_state_changed *ev = cast_position_state_changed(eh); - return zmk_keymap_position_state_changed(ev->position, ev->state); + return zmk_keymap_position_state_changed(ev->position, ev->state, ev->timestamp); #if ZMK_KEYMAP_HAS_SENSORS } else if (is_sensor_event(eh)) { const struct sensor_event *ev = cast_sensor_event(eh); diff --git a/app/src/kscan.c b/app/src/kscan.c index 0046f5c..8575e70 100644 --- a/app/src/kscan.c +++ b/app/src/kscan.c @@ -52,6 +52,7 @@ void zmk_kscan_process_msgq(struct k_work *item) { pos_ev = new_position_state_changed(); pos_ev->state = pressed; pos_ev->position = position; + pos_ev->timestamp = k_uptime_get(); ZMK_EVENT_RAISE(pos_ev); } } diff --git a/app/src/split/bluetooth/central.c b/app/src/split/bluetooth/central.c index cb1b68b..ed52ba0 100644 --- a/app/src/split/bluetooth/central.c +++ b/app/src/split/bluetooth/central.c @@ -60,6 +60,7 @@ static u8_t split_central_notify_func(struct bt_conn *conn, struct bt_gatt_subsc struct position_state_changed *pos_ev = new_position_state_changed(); pos_ev->position = position; pos_ev->state = pressed; + pos_ev->timestamp = k_uptime_get(); LOG_DBG("Trigger key position state change for %d", position); ZMK_EVENT_RAISE(pos_ev); diff --git a/app/tests/hold-tap/balanced/many-nested/events.patterns b/app/tests/hold-tap/balanced/many-nested/events.patterns new file mode 100644 index 0000000..fdf2b15 --- /dev/null +++ b/app/tests/hold-tap/balanced/many-nested/events.patterns @@ -0,0 +1,4 @@ +s/.*hid_listener_keycode/kp/p +s/.*mo_keymap_binding/mo/p +s/.*on_hold_tap_binding/ht_binding/p +s/.*decide_hold_tap/ht_decide/p
\ No newline at end of file diff --git a/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot b/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot new file mode 100644 index 0000000..806896f --- /dev/null +++ b/app/tests/hold-tap/balanced/many-nested/keycode_events.snapshot @@ -0,0 +1,20 @@ +ht_binding_pressed: 0 new undecided hold_tap +ht_decide: 0 decided hold (balanced event 3) +kp_pressed: usage_page 0x07 keycode 0xe1 +ht_binding_pressed: 1 new undecided hold_tap +ht_decide: 1 decided hold (balanced event 3) +kp_pressed: usage_page 0x07 keycode 0xe0 +ht_binding_pressed: 2 new undecided hold_tap +ht_binding_released: 0 cleaning up hold-tap +ht_decide: 2 decided hold (balanced event 3) +kp_pressed: usage_page 0x07 keycode 0xe3 +ht_binding_pressed: 3 new undecided hold_tap +ht_binding_released: 1 cleaning up hold-tap +ht_decide: 3 decided hold (balanced event 3) +kp_pressed: usage_page 0x07 keycode 0xe2 +kp_released: usage_page 0x07 keycode 0xe1 +kp_released: usage_page 0x07 keycode 0xe0 +kp_released: usage_page 0x07 keycode 0xe3 +ht_binding_released: 2 cleaning up hold-tap +kp_released: usage_page 0x07 keycode 0xe2 +ht_binding_released: 3 cleaning up hold-tap diff --git a/app/tests/hold-tap/balanced/many-nested/native_posix.keymap b/app/tests/hold-tap/balanced/many-nested/native_posix.keymap new file mode 100644 index 0000000..3cb04c3 --- /dev/null +++ b/app/tests/hold-tap/balanced/many-nested/native_posix.keymap @@ -0,0 +1,41 @@ +#include <dt-bindings/zmk/keys.h> +#include <behaviors.dtsi> +#include <dt-bindings/zmk/kscan-mock.h> + +/ { + behaviors { + ht_bal: behavior_hold_tap_balanced { + compatible = "zmk,behavior-hold-tap"; + label = "HOLD_TAP_BALANCED"; + #binding-cells = <2>; + flavor = "balanced"; + tapping_term_ms = <300>; + bindings = <&kp>, <&kp>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &ht_bal LSFT F &ht_bal LCTL J + &ht_bal LGUI H &ht_bal LALT L + >; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,100) + ZMK_MOCK_PRESS(0,1,100) + ZMK_MOCK_PRESS(1,0,100) + ZMK_MOCK_PRESS(1,1,100) + ZMK_MOCK_RELEASE(0,0,100) + ZMK_MOCK_RELEASE(0,1,100) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_RELEASE(1,1,100) + >; +}; diff --git a/docs/docs/assets/dev-setup/vscode_devcontainer.png b/docs/docs/assets/dev-setup/vscode_devcontainer.png Binary files differnew file mode 100644 index 0000000..e5c22b0 --- /dev/null +++ b/docs/docs/assets/dev-setup/vscode_devcontainer.png diff --git a/docs/docs/behavior/power.md b/docs/docs/behavior/power.md new file mode 100644 index 0000000..6b8237b --- /dev/null +++ b/docs/docs/behavior/power.md @@ -0,0 +1,64 @@ +--- +title: Power Management Behaviors +sidebar_label: Power Management +--- + +## Summary + +These page contains some of the power management behaviors currently supported by ZMK. + +## External Power Control + +The External power control behavior allows enabling or disabling the VCC power output +to save power. Some of the LEDs will consume power even in OFF state. To preserve +battery life in this scenario, some controller boards have support to disable the +external power completely. + +The following boards currently support this feature: +- nRFMicro +- nice!nano + +## External Power Control Command Defines + +External power control command defines are provided through the [`dt-bindings/zmk/ext_power.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/ext_power.h) header, +which is added at the top of the keymap file: + +``` +#include <dt-bindings/zmk/ext_power.h> +``` + +This will allow you to reference the actions defined in this header such as `EXT_POWER_OFF_CMD`. + +Here is a table describing the command for each define: + +| Define | Action | Alias | +| ------------ | -------------------------------------- | -------- | +| `EXT_POWER_OFF_CMD` | Disable the external power. | `EP_OFF` | +| `EXT_POWER_ON_CMD` | Enable the external power. | `EP_ON` | +| `EXT_POWER_TOGGLE_CMD` | Toggle the external power. | `EP_TOG` | + +### Behavior Binding + +- Reference: `&ext_power` +- Parameter#1: Command, e.g `EP_ON` + +### Example: + +1. Behavior binding to enable the external power + + ``` + &ext_power EP_ON + ``` + +1. Behavior binding to disable the external power + + ``` + &ext_power EP_OFF + ``` + +1. Behavior binding to toggle the external power + + ``` + &ext_power EP_TOG + ``` + diff --git a/docs/docs/customization.md b/docs/docs/customization.md index 0bb1794..261c40d 100644 --- a/docs/docs/customization.md +++ b/docs/docs/customization.md @@ -1,10 +1,21 @@ --- id: customization -title: Customizing ZMK +title: Customizing ZMK/`zmk-config` folders sidebar_label: Customizing ZMK --- After verifying you can successfully flash the default firmware, you will probably want to begin customizing your keymap and other keyboard options. +[In the initial setup tutorial](user-setup), you created a Github repository called `zmk-config`. This repository is a discrete filesystem which works +with the main `zmk` firmware repository to build your desired firmware. The main advantage of a discrete configuration folder is ensuring that the +working components of ZMK are kept separate from your personal keyboard settings, reducing the amount of file manipulation in the configuration process. +This makes flashing ZMK to your keyboard much easier, especially because you don't need to keep an up-to-date copy of zmk on your computer at all times. + +On default `zmk-config` folder should contain two files: +* `<shield>.conf` +* `<shield>`.keymap + +However, your config folder can also be modified to include a `boards/` directory for keymaps and configurations for multiple boards/shields +outside of the default keyboard setting definitions. ## Configuration Changes @@ -26,8 +37,23 @@ GitHub Actions job to build your firmware which you can download once it complet If you need to, a review of [Learn The Basics Of Git In Under 10 Minutes](https://www.freecodecamp.org/news/learn-the-basics-of-git-in-under-10-minutes-da548267cc91/) will help you get these steps right. ::: +## Building from a local `zmk` fork using `zmk-config` + +[As outlined here](dev-build-flash), firmware comes in the form of .uf2 files, which can be built locally using the command `west build`. Normally, +`west build` will default to using the in-tree .keymap and .conf files found in your local copy of the `zmk` repository. However, you can append the command, `-DZMK_CONFIG="C:/the/absolute/path/config"` to `west build` in order to use the contents of your `zmk-config` folder instead of the +default keyboard settings. + **Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.** + + +For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this: + +``` +west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Documents/Github/zmk-config/config" +``` + ## Flashing Your Changes 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#split-keyboard-halves-unable-to-pair). diff --git a/docs/docs/dev-build.md b/docs/docs/dev-build.md index 816468e..83ed8cb 100644 --- a/docs/docs/dev-build.md +++ b/docs/docs/dev-build.md @@ -84,6 +84,19 @@ west build -d build/right -b nice_nano -- -DSHIELD=kyria_right ``` This produces `left` and `right` subfolders under the `build` directory and two separate .uf2 files. For future work on a specific half, use the `-d` parameter again to ensure you are building into the correct location. +### Building from `zmk-config` Folder + +Instead of building .uf2 files using the default keymap and config files, you can build directly from your [`zmk-config` folder](user-setup#github-repo) by adding +`-DZMK_CONFIG="C:/the/absolute/path/config"` to your `west build` command. **Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.** + + +For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this: + +``` +west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Documents/Github/zmk-config/config" +``` + + ## Flashing Once built, the previously supplied parameters will be remembered so you can run the following to flash your diff --git a/docs/docs/dev-guide-new-shield.md b/docs/docs/dev-guide-new-shield.md index 1ba572c..9f7b23c 100644 --- a/docs/docs/dev-guide-new-shield.md +++ b/docs/docs/dev-guide-new-shield.md @@ -21,6 +21,10 @@ The high level steps are: It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/2.3.0/guides/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing. +:::note +ZMK support for split keyboards requires a few more files than single boards to ensure proper connectivity between the central and peripheral units. Check the following guides thoroughly to ensure that all the files are in place. +::: + ## New Shield Directory Shields for Zephyr applications go into the `boards/shields/` directory; since ZMK's Zephyr application lives in the `app/` subdirectory of the repository, that means the new shield directory should be: @@ -45,6 +49,16 @@ config SHIELD_MY_BOARD This will make sure the new configuration `SHIELD_MY_BOARD` is set to true whenever `my_board` is added as a shield in your build. + +**For split boards**, you will need to add configurations for the left and right sides. +``` +config SHIELD_MY_BOARD_LEFT + def_bool $(shields_list_contains,my_board_left) + +config SHIELD_MY_BOARD_RIGHT + def_bool $(shields_list_contains,my_board_right) +``` + ### Kconfig.defconfig The `Kconfig.defconfig` file is where overrides for various configuration settings @@ -63,12 +77,41 @@ config ZMK_KEYBOARD_NAME endif ``` -## Shield Overlay + +Similarly to defining the halves of a split board in `Kconfig.shield` it is important to set the `ZMK_KEYBOARD_NAME` for each half of a split keyboard. + +``` +if SHIELD_MY_BOARD_LEFT + +config ZMK_KEYBOARD_NAME + default "My Awesome Keyboard Left" + +endif + +if SHIELD_MY_BOARD_RIGHT + +config ZMK_KEYBOARD_NAME + default "My Awesome Keyboard Right" + +endif +``` + +## Shield Overlays  + ZMK uses the green color coded pin names to generate devicetree node references. For example, to refer to the node `D0` in the devicetree files, use `&pro_micro_d 0` or to refer to `A1`, use `&pro_micro_a 1`. +<Tabs +defaultValue="unibody" +values={[ +{label: 'Unibody Shields', value: 'unibody'}, +{label: 'Split Shields', value: 'split'}, +]}> + +<TabItem value="unibody"> + The `<shield_name>.overlay` is the devicetree description of the keyboard shield that is merged with the primary board devicetree description before the build. For ZMK, this file at a minimum should include the [chosen]() node named `zmk,kscan` that references a KSCAN driver instance. For a simple 3x3 macropad matrix, this might look something like: @@ -98,6 +141,146 @@ this might look something like: }; ``` +</TabItem> + +<TabItem value="split"> + +### .dtsi files and Shield Overlays (Split Shields) + +Unlike unibody keyboards, split keyboards have a core .dtsi file with shield overlays for each half of the keyboard. +It is preferred to define only the `col-gpios` or `row-gpios` in the common shield .dtsi, depending on the `diode-direction` value. +For `col2row` directed boards like the iris, the shared .dtsi file may look like this: + +``` +#include <dt-bindings/zmk/matrix-transform.h> + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <16>; + rows = <4>; +// | SW6 | SW5 | SW4 | SW3 | SW2 | SW1 | | SW1 | SW2 | SW3 | SW4 | SW5 | SW6 | +// | SW12 | SW11 | SW10 | SW9 | SW8 | SW7 | | SW7 | SW8 | SW9 | SW10 | SW11 | SW12 | +// | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 | | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 | +// | SW24 | SW23 | SW22 | SW21 | SW20 | SW19 | SW25 | | SW25 | SW19 | SW20 | SW21 | SW22 | SW23 | SW24 | +// | SW29 | SW28 | SW27 | SW26 | | SW26 | SW27 | SW28 | SW29 | + map = < +RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) +RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) +RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11) +RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(4,2) RC(4,9) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11) + RC(4,3) RC(4,4) RC(4,5) RC(4,6) RC(4,7) RC(4,8) + >; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + + diode-direction = "col2row"; + row-gpios + = <&pro_micro_d 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row A from the schematic file + , <&pro_micro_d 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row B from the schematic file + , <&pro_micro_d 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row C from the schematic file + , <&pro_micro_d 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row D from the schematic file + , <&pro_micro_d 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row E from the schematic file + ; + + }; +``` + +:::note +Notice that in addition to the common `row-gpios` that are declared in the kscan, the [matrix transform](#optional-matrix-transform) is defined in the .dtsi. +::: + +The missing `col-gpios` would be defined in your `<boardname>_left.overlay` and `<boardname>_right.overlay` files. +Keep in mind that the mirrored position of the GPIOs means that the `col-gpios` will appear reversed when the .overlay files are compared to one another. +Furthermore, the column offset for the [matrix transform](#optional-matrix-transform) should be added to the right half of the keyboard's overlay +because the keyboard's switch matrix is read from left to right, top to bottom. +This is exemplified with the iris .overlay files. + +``` +// iris_left.overlay + +#include "iris.dtsi" // Notice that the main dtsi files are included in the overlay. + +&kscan0 { + col-gpios + = <&pro_micro_a 1 GPIO_ACTIVE_HIGH> // col1 in the schematic + , <&pro_micro_a 0 GPIO_ACTIVE_HIGH> // col2 in the schematic + , <&pro_micro_d 15 GPIO_ACTIVE_HIGH> // col3 in the schematic + , <&pro_micro_d 14 GPIO_ACTIVE_HIGH> // col4 in the schematic + , <&pro_micro_d 16 GPIO_ACTIVE_HIGH> // col5 in the schematic + , <&pro_micro_d 10 GPIO_ACTIVE_HIGH> // col6 in the schematic + ; +}; +``` + +``` +// iris_right.overlay + +#include "iris.dtsi" + +&default_transform { // The matrix transform for this board is 6 columns over because the left half is 6 columns wide according to the matrix. + col-offset = <6>; +}; + +&kscan0 { + col-gpios + = <&pro_micro_d 10 GPIO_ACTIVE_HIGH> // col6 in the schematic + , <&pro_micro_d 16 GPIO_ACTIVE_HIGH> // col5 in the schematic + , <&pro_micro_d 14 GPIO_ACTIVE_HIGH> // col4 in the schematic + , <&pro_micro_d 15 GPIO_ACTIVE_HIGH> // col3 in the schematic + , <&pro_micro_a 0 GPIO_ACTIVE_HIGH> // col2 in the schematic + , <&pro_micro_a 1 GPIO_ACTIVE_HIGH> // col1 in the schematic + ; +}; + +``` + +### .conf files (Split Shields) + +While unibody boards only have one .conf file that applies configuration characteristics to the entire keyboard, +split keyboards are unique in that they contain multiple .conf files with different scopes. +For example, a split board called `my_awesome_split_board` would have the following files: + +* `my_awesome_split_board.conf` - Configuration elements affect both halves +* `my_awesome_split_board_left.conf` - Configuration elements only affect left half +* `my_awesome_split_board_right.conf` - Configuration elements only affect right half + +For proper communication between keyboard halves and that between the central half and the computer, +the **the central and peripheral halves of the keyboard must be defined**. This can be seen below. + +``` +// Central Half (Usually the left side: my_awesome_split_board_left.conf) + +CONFIG_ZMK_SPLIT=y +CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL=y +``` + +``` +// Peripheral Half (Usually the right side: my_awesome_split_board_right.conf) + +CONFIG_ZMK_SPLIT=y +CONFIG_ZMK_SPLIT_BLE_ROLE_Peripheral=y +``` + +Using the .conf file that affects both halves of a split board would be for adding features like deep-sleep or rotary encoders. + +``` +// my_awesome_split_board.conf + +CONFIG_ZMK_SLEEP=y +``` + +</TabItem> +</Tabs> + ## (Optional) Matrix Transform Internally ZMK translates all row/column events into "key position" events to maintain a consistent model that works no matter what any possible GPIO matrix may look like for a certain keyboard. This is particularly helpful when: @@ -238,6 +421,7 @@ If building locally for split boards, you may need to add these lines to the spe <TabItem value = "dtsi"> In your device tree file you will need to add the following lines to define the encoder sensor: + ``` left_encoder: encoder_left { compatible = "alps,ec11"; @@ -247,7 +431,6 @@ left_encoder: encoder_left { resolution = <4>; }; ``` - Here you will have to replace PIN_A and PIN_B with the appropriate pins that your PCB utilizes for the encoder(s). For keyboards that use the Pro Micro or any of the Pro Micro replacements, Sparkfun's [Pro Micro Hookup Guide](https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/hardware-overview-pro-micro) has a pinout diagram that can be useful to determine the right pins. Reference either the blue numbers labeled "Arduino" (digital pins) or the green numbers labeled "Analog" (analog pins). For pins that are labeled as both digital and analog, refer to your specific board's .dtsi file to determine how you should refer to that pin. Add additional encoders as necessary by duplicating the above lines, replacing `left` with whatever you would like to call your encoder, and updating the pins. Note that support for peripheral (right) side sensors over BLE is still in progress. @@ -279,15 +462,12 @@ For split keyboards, make sure to add left hand encoders to the left .overlay fi </TabItem> <TabItem value = "keymap"> -Add the following line to each layer of your keymap file to add default encoder behavior bindings: +Add the following line to your keymap file to add default encoder behavior bindings: ``` sensor-bindings = <&inc_dec_cp M_VOLU M_VOLD>; ``` - -This should be placed after the regular key bindings but within the layer (see the [Default Keymap section](/docs/dev-guide-new-shield#default-keymap) above for an example of where). - -Add additional bindings as necessary to match the default number of encoders on your board. Details on the syntax can be found in the [Encoders](/docs/feature/encoders) and [Keymap](/docs/feature/keymaps) feature documentation. +Add additional bindings as necessary to match the default number of encoders on your board. See the [Encoders](/docs/feature/encoders) and [Keymap](/docs/feature/keymaps) feature documentation for more details. </TabItem> </Tabs> @@ -307,6 +487,11 @@ and then flash with: west flash ``` +:::note +Further testing your keyboard shield without altering the root keymap file can be done with the use of `-DZMK_CONFIG` in your `west build` command, +shown [here](dev-build-flash#building-from-zmk-config-folder) +::: + ## Updating `build.yml` Before publishing your shield to the public via a PR, navigate to `build.yml` found in `.github/workflows` and add your shield to the appropriate list. An example edit to `build.yml` is shown below. @@ -336,7 +521,6 @@ jobs: - board: proton_c shield: clueboard_california ``` - :::note Notice that both the left and right halves of a split board need to be added to the list of shields for proper error checking. :::note diff --git a/docs/docs/dev-setup.md b/docs/docs/dev-setup.md index 4891f5a..521d5d0 100644 --- a/docs/docs/dev-setup.md +++ b/docs/docs/dev-setup.md @@ -16,6 +16,7 @@ values={[ {label: 'macOS', value: 'mac'}, {label: 'Raspberry OS', value: 'raspberryos'}, {label: 'Fedora', value: 'fedora'}, +{label: 'VS Code & Docker', value: 'docker'}, ] }>{props.children}</Tabs>); @@ -167,6 +168,10 @@ Chocolatey is recommended and used for the following instructions. You can manua choco install ninja gperf python git ``` +It is recommended to install `dfu-util` to avoid any later confusion while flashing devices. You can do this by running this command with chocolatey: +``` shell +choco install dfu-util +``` </TabItem> <TabItem value="mac"> @@ -175,10 +180,24 @@ Chocolatey is recommended and used for the following instructions. You can manua Homebrew is required to install the system dependencies. If you haven't done so, visit [Homebrew](https://brew.sh/) for instructions. Once installed, use it to install the base dependencies: ``` -brew install cmake ninja python3 ccache dtc git wget +brew install cmake ninja python3 ccache dtc git wget dfu-util ``` </TabItem> +<TabItem value="docker"> + +This setup leverages the same [image which is used by the GitHub action](https://github.com/zmkfirmware/zephyr-west-action) 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 [VS 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 includes `west` and the compilation toolchain. If you're using docker and VS Code, you can skip right to [Source Code](#source-code). +::: + +</TabItem> </OsTabs> ## Setup @@ -349,12 +368,63 @@ 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 +<OsTabs> +<TabItem value="debian"> + +```sh +cd zmk +``` + +</TabItem> +<TabItem value="raspberryos"> + +```sh +cd zmk +``` + +</TabItem> +<TabItem value="fedora"> + +```sh +cd zmk +``` + +</TabItem> +<TabItem value="mac"> + ```sh cd zmk ``` +</TabItem> +<TabItem value="win"> + +```sh +cd zmk +``` + +</TabItem> + +<TabItem value="docker"> + +Open the `zmk` checkout folder in VS Code. The repository includes a configuration for containerized development, so an alert will pop up: + + + +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! + +:::caution +All subsequent steps must be performed from the VS Code terminal _inside_ the container. +::: + +</TabItem> +</OsTabs> + #### Initialize West ```sh @@ -373,6 +443,17 @@ section again for links to how to do this 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](./dev-build.md). +::: + + #### Export Zephyr™ Core ```sh diff --git a/docs/docs/hardware.md b/docs/docs/hardware.md index faac57c..fe229df 100644 --- a/docs/docs/hardware.md +++ b/docs/docs/hardware.md @@ -29,7 +29,7 @@ That being said, there are currently only a few specific [boards](/docs/faq#what - [Sofle](https://github.com/josefadamcik/SofleKeyboard) (`sofle_left` and `sofle_right`) - [Splitreus62](https://github.com/Na-Cly/splitreus62) (`splitreus62_left` and `splitreus62_right`) - [RoMac+ v4](https://www.littlekeyboards.com/products/romac) (`romac_plus`) -- [RoMac v2](https://mechboards.co.uk/shop/kits/romac-macro-pad/) (`romac') +- [RoMac v2](https://mechboards.co.uk/shop/kits/romac-macro-pad/) (`romac`) - [QAZ](https://www.cbkbd.com/product/qaz-keyboard-kit) (`qaz`) ## Other Hardware diff --git a/docs/docs/user-setup.md b/docs/docs/user-setup.md index 2aade82..2785096 100644 --- a/docs/docs/user-setup.md +++ b/docs/docs/user-setup.md @@ -63,6 +63,7 @@ defaultValue="curl" values={[ {label: 'Using curl', value: 'curl'}, {label: 'Using wget', value: 'wget'}, +{label: 'Using PowerShell', value: 'PowerShell'}, ]}> <TabItem value="curl"> @@ -78,6 +79,12 @@ bash -c "$(wget https://zmkfirmware.dev/setup.sh -O -)" ``` </TabItem> +<TabItem value="PowerShell"> + +``` +iex ((New-Object System.Net.WebClient).DownloadString('https://zmkfirmware.dev/setup.ps1'))" +``` +</TabItem> </Tabs> ### MCU Board Selection diff --git a/docs/sidebars.js b/docs/sidebars.js index 93ce226..12b4a6e 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -23,6 +23,7 @@ module.exports = { "behavior/reset", "behavior/bluetooth", "behavior/lighting", + "behavior/power", ], Development: [ "dev-clean-room", diff --git a/docs/static/setup.ps1 b/docs/static/setup.ps1 index abdb698..5ebd376 100644 --- a/docs/static/setup.ps1 +++ b/docs/static/setup.ps1 @@ -57,7 +57,20 @@ catch [System.Management.Automation.CommandNotFoundException] { }
Test-Git-Config -Option "user.name" -ErrMsg "Git username not set!`nRun: git config --global user.name 'My Name'"
-Test-Git-Config -Option "user.email" -ErrMsg "Git email not set!`nRun: git config --global user.name 'example@myemail.com'"
+Test-Git-Config -Option "user.email" -ErrMsg "Git email not set!`nRun: git config --global user.email 'example@myemail.com'"
+
+$permission = (Get-Acl $pwd).Access |
+?{$_.IdentityReference -match $env:UserName `
+ -and $_.FileSystemRights -match "FullControl" `
+ -or $_.FileSystemRights -match "Write" } |
+
+ Select IdentityReference,FileSystemRights
+
+If (-Not $permission){
+ Write-Host "Sorry, you do not have write permissions in this directory."
+ Write-Host "Please try running this script again from a directory that you do have write permissions for."
+ exit 1
+}
$repo_path = "https://github.com/zmkfirmware/zmk-config-split-template.git"
@@ -78,9 +91,9 @@ Write-Host "Keyboard Shield Selection:" $prompt = "Pick a keyboard"
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
-$options = "Kyria", "Lily58", "Corne", "Splitreus62", "Sofle", "Iris", "RoMac", "makerdiary M60", "Microdox"
-$names = "kyria", "lily58", "corne", "splitreus62", "sofle", "iris", "romac", "m60", "microdox"
-$splits = "y", "y", "y", "y", "y", "y", "n", "n", "y"
+$options = "Kyria", "Lily58", "Corne", "Splitreus62", "Sofle", "Iris", "RoMac", "RoMac+", "makerdiary M60", "Microdox", "TG4X", "QAZ"
+$names = "kyria", "lily58", "corne", "splitreus62", "sofle", "iris", "romac", "romac_plus", "m60", "microdox", "tg4x", "qaz"
+$splits = "y", "y", "y", "y", "y", "y", "n", "n", "n", "y", "n", "n"
$choice = Get-Choice-From-Options -Options $options -Prompt $prompt
$shield_title = $($options[$choice])
diff --git a/docs/static/setup.sh b/docs/static/setup.sh index 49ed3eb..e45a7ed 100644 --- a/docs/static/setup.sh +++ b/docs/static/setup.sh @@ -22,6 +22,13 @@ check_exists "command -v curl" "curl is not installed, and is required for this check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'" check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'" +# Check to see if the user has write permissions in this directory to prevent a cryptic error later on +if [ ! -w `pwd` ]; then + echo 'Sorry, you do not have write permissions in this directory.'; + echo 'Please try running this script again from a directory that you do have write permissions for.'; + exit 1 +fi + repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git" title="ZMK Config Setup:" @@ -51,7 +58,7 @@ echo "" echo "Keyboard Shield Selection:" prompt="Pick an keyboard:" -options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "RoMac" "makerdiary M60" "Microdox") +options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ") PS3="$prompt " # TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos. @@ -67,8 +74,11 @@ select opt in "${options[@]}" "Quit"; do 5 ) shield_title="Sofle" shield="sofle"; split="y"; break;; 6 ) shield_title="Iris" shield="iris"; split="y"; break;; 7 ) shield_title="RoMac" shield="romac"; split="n"; break;; - 8 ) shield_title="M60" shield="m60"; split="n"; break;; - 9 ) shield_title="Microdox" shield="microdox"; split="y"; break;; + 8 ) shield_title="RoMac+" shield="romac_plus"; split="n"; break;; + 9 ) shield_title="M60" shield="m60"; split="n"; break;; + 10 ) shield_title="Microdox" shield="microdox"; split="y"; break;; + 11 ) shield_title="TG4X" shield="tg4x"; split="n"; break;; + 12 ) shield_title="QAZ" shield="qaz"; split="n"; break;; # Add link to docs on adding your own custom shield in your ZMK config! # $(( ${#options[@]}+1 )) ) echo "Other!"; break;; |