From fe3bba38ea482f82c136ca60285791ee04ba8f59 Mon Sep 17 00:00:00 2001 From: Bry Ashman Date: Sat, 14 Feb 2026 19:32:35 +1300 Subject: [PATCH 1/2] feat(core) Add preferred transport changed event Added an event for the preferred transport changing, this is primarily to trigger display updates. --- app/CMakeLists.txt | 1 + .../zmk/events/preferred_transport_changed.h | 18 ++++++++++++++++++ app/src/endpoints.c | 5 ++++- app/src/events/preferred_transport_changed.c | 10 ++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 app/include/zmk/events/preferred_transport_changed.h create mode 100644 app/src/events/preferred_transport_changed.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index cc38244a4c7..d63407cc815 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -73,6 +73,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/conditional_layer.c) target_sources(app PRIVATE src/endpoints.c) target_sources(app PRIVATE src/events/endpoint_changed.c) + target_sources(app PRIVATE src/events/preferred_transport_changed.c) target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/keymap.c) target_sources(app PRIVATE src/events/layer_state_changed.c) diff --git a/app/include/zmk/events/preferred_transport_changed.h b/app/include/zmk/events/preferred_transport_changed.h new file mode 100644 index 00000000000..f65294d41e9 --- /dev/null +++ b/app/include/zmk/events/preferred_transport_changed.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +#include +#include + +struct zmk_preferred_transport_changed { + enum zmk_transport transport; +}; + +ZMK_EVENT_DECLARE(zmk_preferred_transport_changed); \ No newline at end of file diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 17cdc5d7fb8..31e2cad6b6a 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -135,7 +136,8 @@ int zmk_endpoint_set_preferred_transport(enum zmk_transport transport) { endpoints_save_preferred(); - update_current_endpoint(); + raise_zmk_preferred_transport_changed( + (struct zmk_preferred_transport_changed){.transport = preferred_transport}); return 0; } @@ -502,6 +504,7 @@ static int endpoint_listener(const zmk_event_t *eh) { } ZMK_LISTENER(endpoint_listener, endpoint_listener); +ZMK_SUBSCRIPTION(endpoint_listener, zmk_preferred_transport_changed); #if IS_ENABLED(CONFIG_ZMK_USB) ZMK_SUBSCRIPTION(endpoint_listener, zmk_usb_conn_state_changed); #endif diff --git a/app/src/events/preferred_transport_changed.c b/app/src/events/preferred_transport_changed.c new file mode 100644 index 00000000000..8c6bb2d985f --- /dev/null +++ b/app/src/events/preferred_transport_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2026 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_preferred_transport_changed); From b07809a1ed1cc0241cfae5e27d232e30ddf04ced Mon Sep 17 00:00:00 2001 From: Bry Ashman Date: Sat, 14 Feb 2026 19:36:24 +1300 Subject: [PATCH 2/2] fix(display) output status widget shows ble information when preferred Shows the active ble connnection when it is the preferred connection this allows unbonded and inactive connections to be toggled through even if the keyboard has an active connection of another transport type. --- app/src/display/widgets/output_status.c | 91 +++++++++++++++++-------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/app/src/display/widgets/output_status.c b/app/src/display/widgets/output_status.c index 189a7dc248a..f515985864c 100644 --- a/app/src/display/widgets/output_status.c +++ b/app/src/display/widgets/output_status.c @@ -14,15 +14,26 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include +#include #include #include #include +static const char TRANSPORT_SYMBOL_NONE[] = LV_SYMBOL_CLOSE; +static const char TRANSPORT_SYMBOL_BLE[] = LV_SYMBOL_WIFI; +static const char TRANSPORT_SYMBOL_USB[] = LV_SYMBOL_USB; +static const char TRANSPORT_SYMBOL_UNKNOWN[] = "?"; + +static const char ACTIVE_PROFILE_CONNECTED[] = LV_SYMBOL_OK; +static const char ACTIVE_PROFILE_DISCONNECTED[] = LV_SYMBOL_CLOSE; +static const char ACTIVE_PROFILE_UNBONDED[] = LV_SYMBOL_SETTINGS; + static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); struct output_status_state { struct zmk_endpoint_instance selected_endpoint; enum zmk_transport preferred_transport; + int active_profile_index; bool active_profile_connected; bool active_profile_bonded; }; @@ -31,48 +42,65 @@ static struct output_status_state get_state(const zmk_event_t *_eh) { return (struct output_status_state){ .selected_endpoint = zmk_endpoint_get_selected(), .preferred_transport = zmk_endpoint_get_preferred_transport(), + .active_profile_index = zmk_ble_active_profile_index(), .active_profile_connected = zmk_ble_active_profile_is_connected(), .active_profile_bonded = !zmk_ble_active_profile_is_open(), }; } +static const char *symbol_for_transport(enum zmk_transport transport) { + switch (transport) { + case ZMK_TRANSPORT_NONE: + return TRANSPORT_SYMBOL_NONE; + case ZMK_TRANSPORT_BLE: + return TRANSPORT_SYMBOL_BLE; + case ZMK_TRANSPORT_USB: + return TRANSPORT_SYMBOL_USB; + default: + return TRANSPORT_SYMBOL_UNKNOWN; + } +} + +static const char *symbol_for_active_profile_status(const struct output_status_state state) { + if (state.active_profile_bonded) { + if (state.active_profile_connected) { + return ACTIVE_PROFILE_CONNECTED; + } else { + return ACTIVE_PROFILE_DISCONNECTED; + } + } else { + return ACTIVE_PROFILE_UNBONDED; + } +} + static void set_status_symbol(lv_obj_t *label, struct output_status_state state) { char text[20] = {}; enum zmk_transport transport = state.selected_endpoint.transport; - bool connected = transport != ZMK_TRANSPORT_NONE; - - // If we aren't connected, show what we're *trying* to connect to. - if (!connected) { - transport = state.preferred_transport; - } + const char *transport_symbol = symbol_for_transport(transport); + bool disconnected = transport == ZMK_TRANSPORT_NONE; + + // If ble is connected or the preferred_transport show ble information + // This allows you to navigate through the active ble profiles, even when they are unable + // to be connected to. + if (transport == ZMK_TRANSPORT_BLE || state.preferred_transport == ZMK_TRANSPORT_BLE) { + // When the active transport is disconnected always use the ble symbol + // Otherwise display the active transport. + if (disconnected) { + transport_symbol = TRANSPORT_SYMBOL_BLE; + } - switch (transport) { - case ZMK_TRANSPORT_NONE: - strcat(text, LV_SYMBOL_CLOSE); - break; + const char *active_profile_status = symbol_for_active_profile_status(state); - case ZMK_TRANSPORT_USB: - strcat(text, LV_SYMBOL_USB); - if (!connected) { - strcat(text, " " LV_SYMBOL_CLOSE); - } - break; + snprintf(text, sizeof(text), "%s %i %s", transport_symbol, state.active_profile_index + 1, + active_profile_status); - case ZMK_TRANSPORT_BLE: - if (state.active_profile_bonded) { - if (state.active_profile_connected) { - snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_OK, - state.selected_endpoint.ble.profile_index + 1); - } else { - snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_CLOSE, - state.selected_endpoint.ble.profile_index + 1); - } - } else { - snprintf(text, sizeof(text), LV_SYMBOL_WIFI " %i " LV_SYMBOL_SETTINGS, - state.selected_endpoint.ble.profile_index + 1); - } - break; + } else if (disconnected && state.preferred_transport != ZMK_TRANSPORT_NONE) { + // Indicate that we are disconnected from a preferred transport + snprintf(text, sizeof(text), "%s " LV_SYMBOL_CLOSE, + symbol_for_transport(state.preferred_transport)); + } else { + strcat(text, transport_symbol); } lv_label_set_text(label, text); @@ -86,6 +114,9 @@ static void output_status_update_cb(struct output_status_state state) { ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state, output_status_update_cb, get_state) ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_changed); +// Update when the preferred transport changes but doesn't trigger an endpoint change +// eg. Connected to usb with ble preferred to preferred being usb. +ZMK_SUBSCRIPTION(widget_output_status, zmk_preferred_transport_changed); // We don't get an endpoint changed event when the active profile connects/disconnects // but there wasn't another endpoint to switch from/to, so update on BLE events too. #if defined(CONFIG_ZMK_BLE)