diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index cc38244a4..4a592e381 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -57,6 +57,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 770c6ee1c..ee73e4c48 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -692,6 +692,17 @@ config ZMK_KEYMAP_SENSORS_DEFAULT_TRIGGERS_PER_ROTATION endif # ZMK_KEYMAP_SENSORS +choice CBPRINTF_IMPLEMENTATION + default CBPRINTF_NANO + +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/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 653b085d5..cc9d618bf 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -21,6 +21,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 000000000..81447cb31 --- /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 000000000..7d361cb31 --- /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/include/zmk/ble.h b/app/include/zmk/ble.h index 92b2107d8..cb3d96068 100644 --- a/app/include/zmk/ble.h +++ b/app/include/zmk/ble.h @@ -26,6 +26,7 @@ int zmk_ble_prof_select(uint8_t index); void zmk_ble_clear_all_bonds(void); int zmk_ble_prof_disconnect(uint8_t index); +int zmk_ble_last_profile_index(); int zmk_ble_active_profile_index(void); int zmk_ble_profile_index(const bt_addr_le_t *addr); bt_addr_le_t *zmk_ble_profile_address(uint8_t index); diff --git a/app/include/zmk/endpoints.h b/app/include/zmk/endpoints.h index a2ef3181a..e20d19160 100644 --- a/app/include/zmk/endpoints.h +++ b/app/include/zmk/endpoints.h @@ -68,6 +68,8 @@ int zmk_endpoints_toggle_transport(void); */ struct zmk_endpoint_instance zmk_endpoints_selected(void); +struct zmk_endpoint_instance zmk_last_endpoint(); + int zmk_endpoints_send_report(uint16_t usage_page); #if IS_ENABLED(CONFIG_ZMK_POINTING) diff --git a/app/src/behaviors/behavior_last_device.c b/app/src/behaviors/behavior_last_device.c new file mode 100644 index 000000000..100f8934e --- /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 94c1b2936..e7fea1146 100644 --- a/app/src/ble.c +++ b/app/src/ble.c @@ -60,6 +60,7 @@ enum advertising_type { BT_GAP_ADV_FAST_INT_MIN_2, 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 @@ -252,6 +253,7 @@ void zmk_ble_clear_all_bonds(void) { update_advertising(); }; +int zmk_ble_last_profile_index() { return last_profile; } int zmk_ble_active_profile_index(void) { return active_profile; } int zmk_ble_profile_index(const bt_addr_le_t *addr) { @@ -296,6 +298,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 ae0e5e7fd..cfb28b64d 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -27,6 +27,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); COND_CODE_1(IS_ENABLED(CONFIG_ZMK_BLE), (ZMK_TRANSPORT_BLE), (ZMK_TRANSPORT_USB)) static struct zmk_endpoint_instance current_instance = {}; +static struct zmk_endpoint_instance last_instance = {}; static enum zmk_transport preferred_transport = ZMK_TRANSPORT_USB; /* Used if multiple endpoints are ready */ @@ -347,6 +348,7 @@ static void update_current_endpoint(void) { // Cancel all current keypresses so keys don't stay held on the old endpoint. zmk_endpoints_clear_current(); + last_instance = current_instance; current_instance = new_instance; char endpoint_str[ZMK_ENDPOINT_STR_LEN]; diff --git a/docs/docs/keymaps/behaviors/last-device.md b/docs/docs/keymaps/behaviors/last-device.md new file mode 100644 index 000000000..1f65ae4f0 --- /dev/null +++ b/docs/docs/keymaps/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 06bf9fb12..49801d326 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -80,6 +80,7 @@ module.exports = { "keymaps/behaviors/reset", "keymaps/behaviors/bluetooth", "keymaps/behaviors/outputs", + "keymaps/behaviors/last-device", "keymaps/behaviors/underglow", "keymaps/behaviors/backlight", "keymaps/behaviors/power",