diff options
40 files changed, 618 insertions, 134 deletions
diff --git a/.devcontainer/.bashrc b/.devcontainer/.bashrc new file mode 100644 index 0000000..855ea75 --- /dev/null +++ b/.devcontainer/.bashrc @@ -0,0 +1,6 @@ +export LS_OPTIONS='-F --color=auto' +eval "`dircolors`" +alias ls='ls $LS_OPTIONS' +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..940b78b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "name": "ZMK Development", + "dockerFile": "Dockerfile", + "extensions": ["ms-vscode.cpptools"], + "runArgs": ["--security-opt", "label=disable"], + "containerEnv": {"WORKSPACE_DIR": "${containerWorkspaceFolder}"} +} + diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3e59d75..31d28f5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -31,6 +31,7 @@ target_sources(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_DISPLAY app PRIVATE src/display.c) target_sources(app PRIVATE src/event_manager.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/ble_unpair_combo.c) +target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c) target_sources(app PRIVATE src/events/position_state_changed.c) target_sources(app PRIVATE src/events/keycode_state_changed.c) target_sources(app PRIVATE src/events/modifiers_state_changed.c) diff --git a/app/Kconfig b/app/Kconfig index edf5867..fca4912 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -95,6 +95,10 @@ config ZMK_IDLE_SLEEP_TIMEOUT endif +config ZMK_EXT_POWER + bool "Enable support to control external power output" + default y + config ZMK_DISPLAY bool "ZMK display support" default n diff --git a/app/boards/arm/nice_nano/nice_nano.dts b/app/boards/arm/nice_nano/nice_nano.dts index 3ffb0ea..0538b1d 100644 --- a/app/boards/arm/nice_nano/nice_nano.dts +++ b/app/boards/arm/nice_nano/nice_nano.dts @@ -29,6 +29,11 @@ }; }; + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + }; }; &gpiote { diff --git a/app/boards/arm/nrfmicro/nrfmicro_11.dts b/app/boards/arm/nrfmicro/nrfmicro_11.dts index 95bd8ad..87c650e 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_11.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_11.dts @@ -26,6 +26,11 @@ }; }; + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; + }; }; &gpio0 { diff --git a/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts b/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts index 85693a8..ea15b81 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_11_flipped.dts @@ -26,6 +26,11 @@ }; }; + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; + }; }; &gpio0 { diff --git a/app/boards/arm/nrfmicro/nrfmicro_13.dts b/app/boards/arm/nrfmicro/nrfmicro_13.dts index 95bd8ad..ef43946 100644 --- a/app/boards/arm/nrfmicro/nrfmicro_13.dts +++ b/app/boards/arm/nrfmicro/nrfmicro_13.dts @@ -26,6 +26,11 @@ }; }; + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + }; }; &gpio0 { diff --git a/app/boards/arm/nrfmicro/pinmux.c b/app/boards/arm/nrfmicro/pinmux.c index 4e330b6..30117d0 100644 --- a/app/boards/arm/nrfmicro/pinmux.c +++ b/app/boards/arm/nrfmicro/pinmux.c @@ -14,25 +14,14 @@ static int pinmux_nrfmicro_init(struct device *port) { ARG_UNUSED(port); - struct device *p1 = device_get_binding("GPIO_1"); - #if CONFIG_BOARD_NRFMICRO_13 struct device *p0 = device_get_binding("GPIO_0"); - // enable EXT_VCC (use 0 for nRFMicro 1.3, use 1 for nRFMicro 1.1) - gpio_pin_configure(p1, 9, GPIO_OUTPUT); - gpio_pin_set(p1, 9, 0); - #if CONFIG_BOARD_NRFMICRO_CHARGER gpio_pin_configure(p0, 5, GPIO_OUTPUT); gpio_pin_set(p0, 5, 0); #else gpio_pin_configure(p0, 5, GPIO_INPUT); #endif - -#else - // enable EXT_VCC (use 0 for nRFMicro 1.3, use 1 for nRFMicro 1.1) - gpio_pin_configure(p1, 9, GPIO_OUTPUT); - gpio_pin_set(p1, 9, 1); #endif return 0; } diff --git a/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-key-press.yaml b/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-key-press.yaml index bbf3537..6b33910 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-key-press.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-key-press.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2020, Pete Johanson +# Copyright (c) 2020, The ZMK Contributors # SPDX-License-Identifier: MIT description: Sensor rotate key press/release behavior diff --git a/app/dts/bindings/zmk,ext-power-generic.yaml b/app/dts/bindings/zmk,ext-power-generic.yaml new file mode 100644 index 0000000..5a38a09 --- /dev/null +++ b/app/dts/bindings/zmk,ext-power-generic.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2020, The ZMK Contributors +# SPDX-License-Identifier: MIT +# + +description: | + Generic driver for controlling the external power output + by toggling the control-gpio pin status + (Only in supported hardware) + +compatible: "zmk,ext-power-generic" + +properties: + control-gpios: + type: phandle-array + required: true + label: + type: string + required: true + diff --git a/app/dts/bindings/zmk,keymap-sensors.yaml b/app/dts/bindings/zmk,keymap-sensors.yaml index c56361d..86ae5c2 100644 --- a/app/dts/bindings/zmk,keymap-sensors.yaml +++ b/app/dts/bindings/zmk,keymap-sensors.yaml @@ -1,3 +1,8 @@ +# +# Copyright (c) 2020, The ZMK Contributors +# SPDX-License-Identifier: MIT +# + description: | Allows defining the collection of sensors bound in the keymap layers 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/drivers/ext_power.h b/app/include/drivers/ext_power.h new file mode 100644 index 0000000..6c1923e --- /dev/null +++ b/app/include/drivers/ext_power.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include <zephyr/types.h> +#include <stddef.h> +#include <device.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @cond INTERNAL_HIDDEN + * + * Behavior driver API definition and system call entry points. + * + * (Internal use only.) + */ + +typedef int (*ext_power_enable_t)(struct device *dev); +typedef int (*ext_power_disable_t)(struct device *dev); +typedef int (*ext_power_get_t)(struct device *dev); + +__subsystem struct ext_power_api { + ext_power_enable_t enable; + ext_power_disable_t disable; + ext_power_get_t get; +}; +/** + * @endcond + */ + +/** + * @brief Enable the external power output + * @param dev Pointer to the device structure for the driver instance. + * + * @retval 0 If successful. + * @retval Negative errno code if failure. + */ +__syscall int ext_power_enable(struct device *dev); + +static inline int z_impl_ext_power_enable(struct device *dev) { + const struct ext_power_api *api = (const struct ext_power_api *)dev->driver_api; + + if (api->enable == NULL) { + return -ENOTSUP; + } + + return api->enable(dev); +} + +/** + * @brief Disable the external power output + * @param dev Pointer to the device structure for the driver instance. + * + * @retval 0 If successful. + * @retval Negative errno code if failure. + */ +__syscall int ext_power_disable(struct device *dev); + +static inline int z_impl_ext_power_disable(struct device *dev) { + const struct ext_power_api *api = (const struct ext_power_api *)dev->driver_api; + + if (api->disable == NULL) { + return -ENOTSUP; + } + + return api->disable(dev); +} + +/** + * @brief Get the current status of the external power output + * @param dev Pointer to the device structure for the driver instance. + * + * @retval 0 If ext power is disabled. + * @retval 1 if ext power is enabled. + * @retval Negative errno code if failure. + */ +__syscall int ext_power_get(struct device *dev); + +static inline int z_impl_ext_power_get(struct device *dev) { + const struct ext_power_api *api = (const struct ext_power_api *)dev->driver_api; + + if (api->get == NULL) { + return -ENOTSUP; + } + + return api->get(dev); +} + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#include <syscalls/ext_power.h> 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/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..922c157 100644 --- a/app/src/behaviors/behavior_bt.c +++ b/app/src/behaviors/behavior_bt.c @@ -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_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..d691e9f 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 = { 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/ext_power_generic.c b/app/src/ext_power_generic.c new file mode 100644 index 0000000..4817030 --- /dev/null +++ b/app/src/ext_power_generic.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_ext_power_generic + +#include <device.h> +#include <init.h> +#include <drivers/gpio.h> +#include <drivers/ext_power.h> + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +#include <logging/log.h> +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct ext_power_generic_config { + const char *label; + const u8_t pin; + const u8_t flags; +}; + +struct ext_power_generic_data { + struct device *gpio; + bool status; +}; + +static int ext_power_generic_enable(struct device *dev) { + struct ext_power_generic_data *data = dev->driver_data; + const struct ext_power_generic_config *config = dev->config_info; + + if (gpio_pin_set(data->gpio, config->pin, 1)) { + LOG_WRN("Failed to set ext-power control pin"); + return -EIO; + } + data->status = true; + return 0; +} + +static int ext_power_generic_disable(struct device *dev) { + struct ext_power_generic_data *data = dev->driver_data; + const struct ext_power_generic_config *config = dev->config_info; + + if (gpio_pin_set(data->gpio, config->pin, 0)) { + LOG_WRN("Failed to clear ext-power control pin"); + return -EIO; + } + data->status = false; + return 0; +} + +static int ext_power_generic_get(struct device *dev) { + struct ext_power_generic_data *data = dev->driver_data; + return data->status; +} + +static int ext_power_generic_init(struct device *dev) { + struct ext_power_generic_data *data = dev->driver_data; + const struct ext_power_generic_config *config = dev->config_info; + + data->gpio = device_get_binding(config->label); + if (data->gpio == NULL) { + LOG_ERR("Failed to get ext-power control device"); + return -EINVAL; + } + + if (gpio_pin_configure(data->gpio, config->pin, config->flags | GPIO_OUTPUT)) { + LOG_ERR("Failed to configure ext-power control pin"); + return -EIO; + } + + return 0; +} + +static const struct ext_power_generic_config config = { + .label = DT_INST_GPIO_LABEL(0, control_gpios), + .pin = DT_INST_GPIO_PIN(0, control_gpios), + .flags = DT_INST_GPIO_FLAGS(0, control_gpios)}; + +static struct ext_power_generic_data data = {.status = false}; + +static const struct ext_power_api api = {.enable = ext_power_generic_enable, + .disable = ext_power_generic_disable, + .get = ext_power_generic_get}; + +DEVICE_AND_API_INIT(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init, &data, &config, + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &api); + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ 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/main.c b/app/src/main.c index dca923e..0551356 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -15,16 +15,25 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL); #include <zmk/matrix.h> #include <zmk/kscan.h> #include <zmk/display.h> +#include <drivers/ext_power.h> #define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID) void main(void) { + struct device *ext_power; LOG_INF("Welcome to ZMK!\n"); if (zmk_kscan_init(ZMK_KSCAN_DEV) != 0) { return; } + // Enable the external VCC output + ext_power = device_get_binding("EXT_POWER"); + if (ext_power != NULL) { + const struct ext_power_api *ext_power_api = ext_power->driver_api; + ext_power_api->enable(ext_power); + } + #ifdef CONFIG_ZMK_DISPLAY zmk_display_init(); 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/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-setup.md b/docs/docs/dev-setup.md index 4891f5a..114fe0b 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>); @@ -179,6 +180,20 @@ brew install cmake ninja python3 ccache dtc git wget ``` </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 +364,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 +439,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 |