diff --git a/app/include/linker/zmk-rpc-subsystem-settings-reset.ld b/app/include/linker/zmk-rpc-subsystem-persistence.ld similarity index 68% rename from app/include/linker/zmk-rpc-subsystem-settings-reset.ld rename to app/include/linker/zmk-rpc-subsystem-persistence.ld index 207d1c8f5..a74e36b32 100644 --- a/app/include/linker/zmk-rpc-subsystem-settings-reset.ld +++ b/app/include/linker/zmk-rpc-subsystem-persistence.ld @@ -6,4 +6,4 @@ #include -ITERABLE_SECTION_ROM(zmk_rpc_subsystem_settings_reset, 4) +ITERABLE_SECTION_ROM(zmk_rpc_subsystem_persistence, 4) diff --git a/app/include/zmk/keymap.h b/app/include/zmk/keymap.h index 556e04567..472c6f512 100644 --- a/app/include/zmk/keymap.h +++ b/app/include/zmk/keymap.h @@ -73,10 +73,10 @@ int zmk_keymap_set_layer_name(zmk_keymap_layer_id_t id, const char *name, size_t /** * @brief Check if there are any unsaved keymap changes. * - * @retval 0 if there are no changes. - * @retval 1 if there are changes. + * @retval true if there are no changes. + * @retval false if there are changes. */ -int zmk_keymap_check_unsaved_changes(void); +bool zmk_keymap_check_unsaved_changes(void); int zmk_keymap_save_changes(void); int zmk_keymap_discard_changes(void); diff --git a/app/include/zmk/physical_layouts.h b/app/include/zmk/physical_layouts.h index e28c194fe..ba781a251 100644 --- a/app/include/zmk/physical_layouts.h +++ b/app/include/zmk/physical_layouts.h @@ -48,7 +48,7 @@ size_t zmk_physical_layouts_get_list(struct zmk_physical_layout const *const **p int zmk_physical_layouts_select(uint8_t index); int zmk_physical_layouts_get_selected(void); -int zmk_physical_layouts_check_unsaved_selection(void); +bool zmk_physical_layouts_check_unsaved_selection(void); int zmk_physical_layouts_save_selected(void); int zmk_physical_layouts_revert_selected(void); diff --git a/app/include/zmk/studio/rpc.h b/app/include/zmk/studio/rpc.h index e223b7a5a..e71d4e316 100644 --- a/app/include/zmk/studio/rpc.h +++ b/app/include/zmk/studio/rpc.h @@ -56,10 +56,16 @@ struct zmk_rpc_subsystem_handler { enum zmk_studio_rpc_handler_security security; }; -typedef int (*zmk_rpc_subsystem_settings_reset_func)(void); +typedef int (*zmk_rpc_subsystem_reset_settings_func_t)(void); +typedef int (*zmk_rpc_subsystem_discard_changes_func_t)(void); +typedef bool (*zmk_rpc_subsystem_check_unsaved_changes_func_t)(void); +typedef int (*zmk_rpc_subsystem_save_changes_func_t)(void); -struct zmk_rpc_subsystem_settings_reset { - zmk_rpc_subsystem_settings_reset_func callback; +struct zmk_rpc_subsystem_persistence { + zmk_rpc_subsystem_reset_settings_func_t reset_settings; + zmk_rpc_subsystem_check_unsaved_changes_func_t check_unsaved_changes; + zmk_rpc_subsystem_save_changes_func_t save_changes; + zmk_rpc_subsystem_discard_changes_func_t discard_changes; }; /** @@ -109,13 +115,12 @@ struct zmk_rpc_subsystem_settings_reset { .security = _security, \ }; -#define ZMK_RPC_SUBSYSTEM_SETTINGS_RESET(prefix, _callback) \ - STRUCT_SECTION_ITERABLE(zmk_rpc_subsystem_settings_reset, _##prefix##_settings_reset) = { \ - .callback = _callback, \ - }; +#define ZMK_RPC_SUBSYSTEM_PERSISTENCE(prefix, ...) \ + STRUCT_SECTION_ITERABLE(zmk_rpc_subsystem_persistence, \ + _##prefix##_settings_reset) = {__VA_ARGS__}; -#define ZMK_RPC_SUBSYSTEM_SETTINGS_RESET_FOREACH(_var) \ - STRUCT_SECTION_FOREACH(zmk_rpc_subsystem_settings_reset, _var) +#define ZMK_RPC_SUBSYSTEM_PERSISTENCE_FOREACH(_var) \ + STRUCT_SECTION_FOREACH(zmk_rpc_subsystem_persistence, _var) /** * @brief Create a zmk_studio_Notification struct for the given subsystem and type, including diff --git a/app/src/keymap.c b/app/src/keymap.c index a291d5f01..393eb61ec 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -472,23 +472,23 @@ struct zmk_behavior_binding_setting { uint32_t param2; } __packed; -int zmk_keymap_check_unsaved_changes(void) { +bool zmk_keymap_check_unsaved_changes(void) { for (int l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { uint8_t *pending = zmk_keymap_layer_pending_changes[l]; for (int kp = 0; kp < ZMK_KEYMAP_LEN; kp++) { if (pending[kp / 8] & BIT(kp % 8)) { - return 1; + return true; } } #if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) if (settings_layer_orders[l] != keymap_layer_orders[l]) { - return 1; + return true; } #endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) } - return 0; + return false; } #define LAYER_ORDER_SETTINGS_KEY "keymap/layer_order" diff --git a/app/src/physical_layouts.c b/app/src/physical_layouts.c index 31a8ac057..5b113ccd3 100644 --- a/app/src/physical_layouts.c +++ b/app/src/physical_layouts.c @@ -416,14 +416,12 @@ int zmk_physical_layouts_select_initial(void) { return ret; } -int zmk_physical_layouts_check_unsaved_selection(void) { +bool zmk_physical_layouts_check_unsaved_selection(void) { #if IS_ENABLED(CONFIG_SETTINGS) - return saved_selected_index < 0 || - saved_selected_index == (uint8_t)zmk_physical_layouts_get_selected() - ? 0 - : 1; + return !(saved_selected_index < 0 || + saved_selected_index == (uint8_t)zmk_physical_layouts_get_selected()); #else - return -ENOTSUP; + return false; #endif } diff --git a/app/src/studio/CMakeLists.txt b/app/src/studio/CMakeLists.txt index a8417dd76..444c9a29c 100644 --- a/app/src/studio/CMakeLists.txt +++ b/app/src/studio/CMakeLists.txt @@ -3,7 +3,7 @@ zephyr_linker_sources(DATA_SECTIONS ../../include/linker/zmk-rpc-subsystems.ld) zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-subsystem-handlers.ld) -zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-subsystem-settings-reset.ld) +zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-subsystem-persistence.ld) zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-event-mappers.ld) zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-transport.ld) diff --git a/app/src/studio/core_subsystem.c b/app/src/studio/core_subsystem.c index 1739919a6..664c5796b 100644 --- a/app/src/studio/core_subsystem.c +++ b/app/src/studio/core_subsystem.c @@ -65,8 +65,8 @@ zmk_studio_Response get_lock_state(const zmk_studio_Request *req) { zmk_studio_Response reset_settings(const zmk_studio_Request *req) { LOG_DBG(""); - ZMK_RPC_SUBSYSTEM_SETTINGS_RESET_FOREACH(sub) { - int ret = sub->callback(); + ZMK_RPC_SUBSYSTEM_PERSISTENCE_FOREACH(sub) { + int ret = sub->reset_settings(); if (ret < 0) { LOG_ERR("Failed to reset settings: %d", ret); return CORE_RESPONSE(reset_settings, false); @@ -76,9 +76,68 @@ zmk_studio_Response reset_settings(const zmk_studio_Request *req) { return CORE_RESPONSE(reset_settings, true); } +static zmk_studio_Response check_unsaved_changes(const zmk_studio_Request *req) { + LOG_DBG(""); + bool unsaved = false; + ZMK_RPC_SUBSYSTEM_PERSISTENCE_FOREACH(p) { + unsaved = p->check_unsaved_changes(); + if (unsaved) { + break; + } + } + + return CORE_RESPONSE(check_unsaved_changes, unsaved); +} + +static zmk_studio_Response save_changes(const zmk_studio_Request *req) { + LOG_DBG(""); + + zmk_core_SaveChangesResponse resp = zmk_core_SaveChangesResponse_init_zero; + resp.which_result = zmk_core_SaveChangesResponse_ok_tag; + resp.result.ok = true; + + ZMK_RPC_SUBSYSTEM_PERSISTENCE_FOREACH(p) { + int ret = p->save_changes(); + if (ret < 0) { + resp.which_result = zmk_core_SaveChangesResponse_err_tag; + switch (ret) { + case -ENOTSUP: + resp.result.err = zmk_core_SaveChangesErrorCode_SAVE_CHANGES_ERR_NOT_SUPPORTED; + break; + case -ENOSPC: + resp.result.err = zmk_core_SaveChangesErrorCode_SAVE_CHANGES_ERR_NO_SPACE; + break; + default: + resp.result.err = zmk_core_SaveChangesErrorCode_SAVE_CHANGES_ERR_GENERIC; + break; + } + break; + } + } + + return CORE_RESPONSE(save_changes, resp); +} + +static zmk_studio_Response discard_changes(const zmk_studio_Request *req) { + LOG_DBG(""); + int ret = 0; + ZMK_RPC_SUBSYSTEM_PERSISTENCE_FOREACH(p) { + ret = p->discard_changes(); + if (ret < 0) { + LOG_ERR("Failed to discard changes for subsystem %p: %d", p, ret); + break; + } + } + + return CORE_RESPONSE(discard_changes, ret >= 0); +} + ZMK_RPC_SUBSYSTEM_HANDLER(core, get_device_info, ZMK_STUDIO_RPC_HANDLER_UNSECURED); ZMK_RPC_SUBSYSTEM_HANDLER(core, get_lock_state, ZMK_STUDIO_RPC_HANDLER_UNSECURED); ZMK_RPC_SUBSYSTEM_HANDLER(core, reset_settings, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(core, check_unsaved_changes, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(core, save_changes, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(core, discard_changes, ZMK_STUDIO_RPC_HANDLER_SECURED); static int core_event_mapper(const zmk_event_t *eh, zmk_studio_Notification *n) { struct zmk_studio_core_lock_state_changed *lock_ev = as_zmk_studio_core_lock_state_changed(eh); diff --git a/app/src/studio/keymap_subsystem.c b/app/src/studio/keymap_subsystem.c index 1d51975c8..f1a23e94a 100644 --- a/app/src/studio/keymap_subsystem.c +++ b/app/src/studio/keymap_subsystem.c @@ -172,12 +172,15 @@ zmk_studio_Response set_layer_binding(const zmk_studio_Request *req) { zmk_keymap_SetLayerBindingResponse_SET_LAYER_BINDING_RESP_OK); } +static bool keymap_check_unsaved_changes(void) { + return zmk_physical_layouts_check_unsaved_selection() || zmk_keymap_check_unsaved_changes(); +} + zmk_studio_Response check_unsaved_changes(const zmk_studio_Request *req) { LOG_DBG(""); - int layout_changes = zmk_physical_layouts_check_unsaved_selection(); - int keymap_changes = zmk_keymap_check_unsaved_changes(); + bool unsaved = keymap_check_unsaved_changes(); - return KEYMAP_RESPONSE(check_unsaved_changes, layout_changes > 0 || keymap_changes > 0); + return KEYMAP_RESPONSE(check_unsaved_changes, unsaved); } static void map_errno_to_save_resp(int err, zmk_keymap_SaveChangesResponse *resp) { @@ -196,23 +199,32 @@ static void map_errno_to_save_resp(int err, zmk_keymap_SaveChangesResponse *resp } } +static int keymap_subsys_save_changes(void) { + int ret = zmk_physical_layouts_save_selected(); + + if (ret < 0) { + LOG_WRN("Failed to save selected physical layout (%d)", ret); + return ret; + } + + ret = zmk_keymap_save_changes(); + if (ret < 0) { + LOG_WRN("Failed to save keymap changes (%d)", ret); + return ret; + } + + return 0; +} + zmk_studio_Response save_changes(const zmk_studio_Request *req) { zmk_keymap_SaveChangesResponse resp = zmk_keymap_SaveChangesResponse_init_zero; resp.which_result = zmk_keymap_SaveChangesResponse_ok_tag; resp.result.ok = true; LOG_DBG(""); - int ret = zmk_physical_layouts_save_selected(); + int ret = keymap_subsys_save_changes(); if (ret < 0) { - LOG_WRN("Failed to save selected physical layout (%d)", ret); - map_errno_to_save_resp(ret, &resp); - return KEYMAP_RESPONSE(save_changes, resp); - } - - ret = zmk_keymap_save_changes(); - if (ret < 0) { - LOG_WRN("Failed to save keymap changes (%d)", ret); map_errno_to_save_resp(ret, &resp); return KEYMAP_RESPONSE(save_changes, resp); } @@ -223,14 +235,25 @@ zmk_studio_Response save_changes(const zmk_studio_Request *req) { return KEYMAP_RESPONSE(save_changes, resp); } -zmk_studio_Response discard_changes(const zmk_studio_Request *req) { - LOG_DBG(""); +static int keymap_subsys_discard_changes(void) { int ret = zmk_physical_layouts_revert_selected(); if (ret < 0) { - return ZMK_RPC_SIMPLE_ERR(GENERIC); + LOG_ERR("Failed to discard physical layout changes (%d)", ret); + return ret; } ret = zmk_keymap_discard_changes(); + if (ret < 0) { + LOG_ERR("Failed to discard keymap changes (%d)", ret); + return ret; + } + + return 0; +} + +zmk_studio_Response discard_changes(const zmk_studio_Request *req) { + LOG_DBG(""); + int ret = keymap_subsys_discard_changes(); if (ret < 0) { return ZMK_RPC_SIMPLE_ERR(GENERIC); } @@ -243,7 +266,10 @@ zmk_studio_Response discard_changes(const zmk_studio_Request *req) { static int keymap_settings_reset(void) { return zmk_keymap_reset_settings(); } -ZMK_RPC_SUBSYSTEM_SETTINGS_RESET(keymap, keymap_settings_reset); +ZMK_RPC_SUBSYSTEM_PERSISTENCE(keymap, .reset_settings = keymap_settings_reset, + .check_unsaved_changes = keymap_check_unsaved_changes, + .save_changes = keymap_subsys_save_changes, + .discard_changes = keymap_subsys_discard_changes, ); static bool encode_layout_name(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { struct zmk_physical_layout *layout = (struct zmk_physical_layout *)*arg;