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/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) 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);