From b043f2ad153b764e64a0ff1f1eb53cca47b9e5a8 Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Fri, 2 Jun 2023 20:54:54 -0400 Subject: [PATCH] Last device behavior --- .github/workflows/build-user-config.yml | 2 + app/CMakeLists.txt | 2 + app/Kconfig | 6 ++ .../dts/bindings/gpio/microchip,mcp23017.yaml | 29 ++++++ app/dts/behaviors.dtsi | 1 + app/dts/behaviors/last_device.dtsi | 17 ++++ .../behaviors/zmk,behavior-last-device.yaml | 8 ++ .../behaviors/zmk,behavior-macro.yaml | 1 + app/include/zmk/ble.h | 2 + app/include/zmk/endpoints.h | 2 + app/src/behaviors/behavior_last_device.c | 96 +++++++++++++++++++ app/src/ble.c | 7 ++ app/src/endpoints.c | 4 + docs/docs/behaviors/last-device.md | 19 ++++ docs/sidebars.js | 1 + 15 files changed, 197 insertions(+) create mode 100644 app/drivers/zephyr/dts/bindings/gpio/microchip,mcp23017.yaml create mode 100644 app/dts/behaviors/last_device.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-last-device.yaml create mode 100644 app/src/behaviors/behavior_last_device.c create mode 100644 docs/docs/behaviors/last-device.md diff --git a/.github/workflows/build-user-config.yml b/.github/workflows/build-user-config.yml index 5891ddc1..a5226e70 100644 --- a/.github/workflows/build-user-config.yml +++ b/.github/workflows/build-user-config.yml @@ -18,6 +18,8 @@ on: default: "bin" required: false type: string + artifact_name: + description: "Artifact output file name" archive_name: description: "Archive output file name" default: "firmware" diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index a647e883..711c3929 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -47,6 +47,8 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c) target_sources(app PRIVATE src/behaviors/behavior_outputs.c) + target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_LAST_DEVICE app PRIVATE src/behaviors/behavior_last_device.c) + target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c) target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c) target_sources(app PRIVATE src/behaviors/behavior_to_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) diff --git a/app/Kconfig b/app/Kconfig index d1b6682f..1218a79c 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -523,6 +523,12 @@ choice CBPRINTF_IMPLEMENTATION endchoice +DT_COMPAT_ZMK_BEHAVIOR_LAST_DEVICE := zmk,behavior-last-device + +config ZMK_BEHAVIOR_LAST_DEVICE + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_LAST_DEVICE)) + module = ZMK module-str = zmk source "subsys/logging/Kconfig.template.log_config" diff --git a/app/drivers/zephyr/dts/bindings/gpio/microchip,mcp23017.yaml b/app/drivers/zephyr/dts/bindings/gpio/microchip,mcp23017.yaml new file mode 100644 index 00000000..2d2b93d8 --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/gpio/microchip,mcp23017.yaml @@ -0,0 +1,29 @@ +# +# Copyright (c) 2020 Geanix ApS +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: > + This is a representation of the Microchip MCP23017 I2C Gpio Expander. + +compatible: "microchip,mcp23017" + +include: [gpio-controller.yaml, i2c-device.yaml] + +properties: + label: + required: true + + "#gpio-cells": + const: 2 + + ngpios: + type: int + required: true + const: 16 + description: Number of gpios supported + +gpio-cells: + - pin + - flags diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index b3502cbb..c7b59d16 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/app/dts/behaviors/last_device.dtsi b/app/dts/behaviors/last_device.dtsi new file mode 100644 index 00000000..81447cb3 --- /dev/null +++ b/app/dts/behaviors/last_device.dtsi @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + behaviors { + /omit-if-no-ref/ last_dev: last_device { + compatible = "zmk,behavior-last-device"; + label = "LAST_DEVICE"; + #binding-cells = <0>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-last-device.yaml b/app/dts/bindings/behaviors/zmk,behavior-last-device.yaml new file mode 100644 index 00000000..7d361cb3 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-last-device.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Last Device Behavior + +compatible: "zmk,behavior-last-device" + +include: zero_param.yaml diff --git a/app/dts/bindings/behaviors/zmk,behavior-macro.yaml b/app/dts/bindings/behaviors/zmk,behavior-macro.yaml index e6f6757d..ed6049f4 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-macro.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-macro.yaml @@ -16,4 +16,5 @@ properties: description: The default time to wait (in milliseconds) before triggering the next behavior in the macro bindings list. tap-ms: type: int + default: 100 description: The default time to wait (in milliseconds) between the press and release events on a tapped macro behavior binding diff --git a/app/include/zmk/ble.h b/app/include/zmk/ble.h index 1c84777d..c6d0a075 100644 --- a/app/include/zmk/ble.h +++ b/app/include/zmk/ble.h @@ -25,8 +25,10 @@ int zmk_ble_prof_next(); int zmk_ble_prof_prev(); int zmk_ble_prof_select(uint8_t index); +int zmk_ble_last_profile_index(); int zmk_ble_active_profile_index(); bt_addr_le_t *zmk_ble_active_profile_addr(); +bool zmk_ble_profile_is_open(uint8_t profile); bool zmk_ble_active_profile_is_open(); bool zmk_ble_active_profile_is_connected(); char *zmk_ble_active_profile_name(); diff --git a/app/include/zmk/endpoints.h b/app/include/zmk/endpoints.h index c8860533..08b77d26 100644 --- a/app/include/zmk/endpoints.h +++ b/app/include/zmk/endpoints.h @@ -10,6 +10,8 @@ int zmk_endpoints_select(enum zmk_endpoint endpoint); int zmk_endpoints_toggle(); +enum zmk_endpoint zmk_preferred_endpoint(); +enum zmk_endpoint zmk_last_endpoint(); enum zmk_endpoint zmk_endpoints_selected(); int zmk_endpoints_send_report(uint16_t usage_page); diff --git a/app/src/behaviors/behavior_last_device.c b/app/src/behaviors/behavior_last_device.c new file mode 100644 index 00000000..100f8934 --- /dev/null +++ b/app/src/behaviors/behavior_last_device.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define DT_DRV_COMPAT zmk_behavior_last_device + +int8_t last_device; +bool skip_next_endpoint_change = false; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + if (last_device == -1) { + LOG_DBG("Toggling output"); + zmk_endpoints_toggle(); + } else { + LOG_DBG("Switching to last ble device: %d", last_device); + zmk_ble_prof_select(last_device); + if (zmk_endpoints_selected() == ZMK_ENDPOINT_USB) { + LOG_DBG("Toggling output"); + zmk_endpoints_toggle(); + } + } + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int behavior_last_device_init(const struct device *dev) { return 0; } + +static const struct behavior_driver_api behavior_last_device_driver_api = { + .binding_pressed = on_keymap_binding_pressed, +}; + +static void update_last_device_ble_index(uint8_t profile) { + if (!zmk_ble_profile_is_open(profile)) { + last_device = profile; + LOG_DBG("Last device set to %d", last_device); + } +}; + +static int last_device_listener(const zmk_event_t *eh) { + if (as_zmk_endpoint_selection_changed(eh) != NULL) { + if (zmk_preferred_endpoint() == zmk_endpoints_selected()) { + if (!skip_next_endpoint_change) { + if (zmk_endpoints_selected() == ZMK_ENDPOINT_USB) { + update_last_device_ble_index(zmk_ble_active_profile_index()); + } else { + last_device = -1; + LOG_DBG("Last device set to %d", last_device); + } + } else { + skip_next_endpoint_change = false; + } + } else if (zmk_endpoints_selected() == ZMK_ENDPOINT_BLE && + zmk_preferred_endpoint() == ZMK_ENDPOINT_USB) { + LOG_DBG("USB disconnected"); + update_last_device_ble_index(zmk_ble_last_profile_index()); + } else { + LOG_DBG("Skipping next endpoint change"); + skip_next_endpoint_change = true; + } + } + if (as_zmk_ble_active_profile_changed(eh) != NULL && + zmk_endpoints_selected() == ZMK_ENDPOINT_BLE) { + update_last_device_ble_index(zmk_ble_last_profile_index()); + } + return 0; +} + +ZMK_LISTENER(last_device_listener, last_device_listener); +ZMK_SUBSCRIPTION(last_device_listener, zmk_endpoint_selection_changed); +ZMK_SUBSCRIPTION(last_device_listener, zmk_ble_active_profile_changed); + +#define LAST_DEVICE_INST(n) \ + DEVICE_DT_INST_DEFINE(n, behavior_last_device_init, NULL, NULL, NULL, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_last_device_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(LAST_DEVICE_INST) diff --git a/app/src/ble.c b/app/src/ble.c index c7bdc401..1e33b79e 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -66,6 +66,7 @@ enum advertising_type { BT_GAP_ADV_FAST_INT_MAX_2, NULL) static struct zmk_ble_profile profiles[ZMK_BLE_PROFILE_COUNT]; +static uint8_t last_profile; static uint8_t active_profile; #define DEVICE_NAME CONFIG_BT_DEVICE_NAME @@ -99,6 +100,10 @@ static void raise_profile_changed_event_callback(struct k_work *work) { K_WORK_DEFINE(raise_profile_changed_event_work, raise_profile_changed_event_callback); +bool zmk_ble_profile_is_open(uint8_t profile) { + return !bt_addr_le_cmp(&profiles[profile].peer, BT_ADDR_LE_ANY); +} + bool zmk_ble_active_profile_is_open() { return !bt_addr_le_cmp(&profiles[active_profile].peer, BT_ADDR_LE_ANY); } @@ -228,6 +233,7 @@ int zmk_ble_clear_bonds() { return 0; }; +int zmk_ble_last_profile_index() { return last_profile; } int zmk_ble_active_profile_index() { return active_profile; } #if IS_ENABLED(CONFIG_SETTINGS) @@ -256,6 +262,7 @@ int zmk_ble_prof_select(uint8_t index) { return 0; } + last_profile = active_profile; active_profile = index; ble_save_profile(); diff --git a/app/src/endpoints.c b/app/src/endpoints.c index dbd1a3e6..55abd11a 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -24,6 +24,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #define DEFAULT_ENDPOINT \ COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ZMK_ENDPOINT_BLE), (ZMK_ENDPOINT_USB)) +static enum zmk_endpoint last_endpoint = DEFAULT_ENDPOINT; static enum zmk_endpoint current_endpoint = DEFAULT_ENDPOINT; static enum zmk_endpoint preferred_endpoint = ZMK_ENDPOINT_USB; /* Used if multiple endpoints are ready */ @@ -62,6 +63,8 @@ int zmk_endpoints_select(enum zmk_endpoint endpoint) { return 0; } +enum zmk_endpoint zmk_last_endpoint() { return last_endpoint; } +enum zmk_endpoint zmk_preferred_endpoint() { return preferred_endpoint; } enum zmk_endpoint zmk_endpoints_selected() { return current_endpoint; } int zmk_endpoints_toggle() { @@ -240,6 +243,7 @@ static void update_current_endpoint() { /* Cancel all current keypresses so keys don't stay held on the old endpoint. */ disconnect_current_endpoint(); + last_endpoint = current_endpoint; current_endpoint = new_endpoint; LOG_INF("Endpoint changed: %d", current_endpoint); diff --git a/docs/docs/behaviors/last-device.md b/docs/docs/behaviors/last-device.md new file mode 100644 index 00000000..1f65ae4f --- /dev/null +++ b/docs/docs/behaviors/last-device.md @@ -0,0 +1,19 @@ +--- +title: Last Device Behavior +sidebar_label: Reset +--- + +## Summary + +The last device behavior switches to the last connected device, whether it be bluetooth or USB. + +### Behavior Binding + +- Reference: `&last_dev` +- Parameters: None + +Example: + +``` +&last_dev +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index 43f17b41..17f9bc1b 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -39,6 +39,7 @@ module.exports = { "behaviors/reset", "behaviors/bluetooth", "behaviors/outputs", + "behaviors/last-device", "behaviors/underglow", "behaviors/backlight", "behaviors/power",