diff options
Diffstat (limited to 'app/src')
-rw-r--r-- | app/src/keymap.c | 649 | ||||
-rw-r--r-- | app/src/physical_layouts.c | 37 | ||||
-rw-r--r-- | app/src/studio/CMakeLists.txt | 2 | ||||
-rw-r--r-- | app/src/studio/Kconfig | 2 | ||||
-rw-r--r-- | app/src/studio/core_subsystem.c | 13 | ||||
-rw-r--r-- | app/src/studio/keymap_subsystem.c | 539 |
6 files changed, 1192 insertions, 50 deletions
diff --git a/app/src/keymap.c b/app/src/keymap.c index 94bd12048c..7afd951c03 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -6,10 +6,12 @@ #include <drivers/behavior.h> #include <zephyr/sys/util.h> +#include <zephyr/settings/settings.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/logging/log.h> LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#include <zmk/stdlib.h> #include <zmk/behavior.h> #include <zmk/keymap.h> #include <zmk/matrix.h> @@ -27,7 +29,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include <zmk/events/sensor_event.h> static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0; -static uint8_t _zmk_keymap_layer_default = 0; +static zmk_keymap_layer_id_t _zmk_keymap_layer_default = 0; #define DT_DRV_COMPAT zmk_keymap @@ -38,7 +40,11 @@ static uint8_t _zmk_keymap_layer_default = 0; #endif #define TRANSFORMED_LAYER(node) \ - { LISTIFY(DT_PROP_LEN(node, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), node) } + { \ + COND_CODE_1( \ + DT_NODE_HAS_PROP(node, bindings), \ + (LISTIFY(DT_PROP_LEN(node, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), node)), ()) \ + } #if ZMK_KEYMAP_HAS_SENSORS #define _TRANSFORM_SENSOR_ENTRY(idx, layer) \ @@ -58,7 +64,7 @@ static uint8_t _zmk_keymap_layer_default = 0; #endif /* ZMK_KEYMAP_HAS_SENSORS */ -#define LAYER_NAME(node) DT_PROP_OR(node, display_name, DT_PROP_OR(node, label, NULL)) +#define LAYER_NAME(node) DT_PROP_OR(node, display_name, DT_PROP_OR(node, label, "")) // State @@ -67,12 +73,36 @@ static uint8_t _zmk_keymap_layer_default = 0; // still send the release event to the behavior in that layer also. static uint32_t zmk_keymap_active_behavior_layer[ZMK_KEYMAP_LEN]; -static struct zmk_behavior_binding zmk_keymap[ZMK_KEYMAP_LAYERS_LEN][ZMK_KEYMAP_LEN] = { - DT_INST_FOREACH_CHILD_SEP(0, TRANSFORMED_LAYER, (, ))}; +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +static uint8_t keymap_layer_orders[ZMK_KEYMAP_LAYERS_LEN]; + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +#define KEYMAP_VAR(_name, _opts) \ + static _opts struct zmk_behavior_binding _name[ZMK_KEYMAP_LAYERS_LEN][ZMK_KEYMAP_LEN] = { \ + COND_CODE_1(IS_ENABLED(CONFIG_ZMK_STUDIO), \ + (DT_INST_FOREACH_CHILD_SEP(0, TRANSFORMED_LAYER, (, ))), \ + (DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(0, TRANSFORMED_LAYER, (, ))))}; + +KEYMAP_VAR(zmk_keymap, ) + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +KEYMAP_VAR(zmk_stock_keymap, const) + +static char zmk_keymap_layer_names[ZMK_KEYMAP_LAYERS_LEN][CONFIG_ZMK_KEYMAP_LAYER_NAME_MAX_LEN] = { + DT_INST_FOREACH_CHILD_SEP(0, LAYER_NAME, (, ))}; + +static uint32_t changed_layer_names = 0; + +#else static const char *zmk_keymap_layer_names[ZMK_KEYMAP_LAYERS_LEN] = { DT_INST_FOREACH_CHILD_SEP(0, LAYER_NAME, (, ))}; +#endif + #if ZMK_KEYMAP_HAS_SENSORS static struct zmk_behavior_binding @@ -81,23 +111,50 @@ static struct zmk_behavior_binding #endif /* ZMK_KEYMAP_HAS_SENSORS */ -static inline int set_layer_state(uint8_t layer, bool state) { +#define ASSERT_LAYER_VAL(_layer, _fail_ret) \ + if ((_layer) >= ZMK_KEYMAP_LAYERS_LEN) { \ + return (_fail_ret); \ + } + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +uint8_t map_layer_id_to_index(zmk_keymap_layer_id_t layer_id) { + for (uint8_t i = 0; i < ZMK_KEYMAP_LAYERS_LEN; i++) { + if (keymap_layer_orders[i] == layer_id) { + return i; + } + } + + return ZMK_KEYMAP_LAYER_ID_INVAL; +} + +#define LAYER_INDEX_TO_ID(_layer) keymap_layer_orders[_layer] +#define LAYER_ID_TO_INDEX(_layer) map_layer_id_to_index(_layer) + +#else + +#define LAYER_INDEX_TO_ID(_layer) _layer +#define LAYER_ID_TO_INDEX(_layer) _layer + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +static inline int set_layer_state(zmk_keymap_layer_id_t layer_id, bool state) { int ret = 0; - if (layer >= ZMK_KEYMAP_LAYERS_LEN) { + if (layer_id >= ZMK_KEYMAP_LAYERS_LEN) { return -EINVAL; } // Default layer should *always* remain active - if (layer == _zmk_keymap_layer_default && !state) { + if (layer_id == _zmk_keymap_layer_default && !state) { return 0; } zmk_keymap_layers_state_t old_state = _zmk_keymap_layer_state; - WRITE_BIT(_zmk_keymap_layer_state, layer, state); + WRITE_BIT(_zmk_keymap_layer_state, layer_id, state); // Don't send state changes unless there was an actual change if (old_state != _zmk_keymap_layer_state) { - LOG_DBG("layer_changed: layer %d state %d", layer, state); - ret = raise_layer_state_changed(layer, state); + LOG_DBG("layer_changed: layer %d state %d", layer_id, state); + ret = raise_layer_state_changed(layer_id, state); if (ret < 0) { LOG_WRN("Failed to raise layer state changed (%d)", ret); } @@ -106,21 +163,28 @@ static inline int set_layer_state(uint8_t layer, bool state) { return ret; } -uint8_t zmk_keymap_layer_default(void) { return _zmk_keymap_layer_default; } +zmk_keymap_layer_id_t zmk_keymap_layer_index_to_id(zmk_keymap_layer_index_t layer_index) { + ASSERT_LAYER_VAL(layer_index, UINT8_MAX); + + return LAYER_INDEX_TO_ID(layer_index); +} + +zmk_keymap_layer_id_t zmk_keymap_layer_default(void) { return _zmk_keymap_layer_default; } zmk_keymap_layers_state_t zmk_keymap_layer_state(void) { return _zmk_keymap_layer_state; } -bool zmk_keymap_layer_active_with_state(uint8_t layer, zmk_keymap_layers_state_t state_to_test) { +bool zmk_keymap_layer_active_with_state(zmk_keymap_layer_id_t layer, + zmk_keymap_layers_state_t state_to_test) { // The default layer is assumed to be ALWAYS ACTIVE so we include an || here to ensure nobody // breaks up that assumption by accident return (state_to_test & (BIT(layer))) == (BIT(layer)) || layer == _zmk_keymap_layer_default; }; -bool zmk_keymap_layer_active(uint8_t layer) { +bool zmk_keymap_layer_active(zmk_keymap_layer_id_t layer) { return zmk_keymap_layer_active_with_state(layer, _zmk_keymap_layer_state); }; -uint8_t zmk_keymap_highest_layer_active(void) { +zmk_keymap_layer_id_t zmk_keymap_highest_layer_active(void) { for (uint8_t layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer > 0; layer--) { if (zmk_keymap_layer_active(layer)) { return layer; @@ -129,11 +193,13 @@ uint8_t zmk_keymap_highest_layer_active(void) { return zmk_keymap_layer_default(); } -int zmk_keymap_layer_activate(uint8_t layer) { return set_layer_state(layer, true); }; +int zmk_keymap_layer_activate(zmk_keymap_layer_id_t layer) { return set_layer_state(layer, true); }; -int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false); }; +int zmk_keymap_layer_deactivate(zmk_keymap_layer_id_t layer) { + return set_layer_state(layer, false); +}; -int zmk_keymap_layer_toggle(uint8_t layer) { +int zmk_keymap_layer_toggle(zmk_keymap_layer_id_t layer) { if (zmk_keymap_layer_active(layer)) { return zmk_keymap_layer_deactivate(layer); } @@ -141,7 +207,7 @@ int zmk_keymap_layer_toggle(uint8_t layer) { return zmk_keymap_layer_activate(layer); }; -int zmk_keymap_layer_to(uint8_t layer) { +int zmk_keymap_layer_to(zmk_keymap_layer_id_t layer) { for (int i = ZMK_KEYMAP_LAYERS_LEN - 1; i >= 0; i--) { zmk_keymap_layer_deactivate(i); } @@ -151,18 +217,365 @@ int zmk_keymap_layer_to(uint8_t layer) { return 0; } -bool is_active_layer(uint8_t layer, zmk_keymap_layers_state_t layer_state) { - return (layer_state & BIT(layer)) == BIT(layer) || layer == _zmk_keymap_layer_default; +const char *zmk_keymap_layer_name(zmk_keymap_layer_id_t layer_id) { + ASSERT_LAYER_VAL(layer_id, NULL) + + return zmk_keymap_layer_names[layer_id]; } -const char *zmk_keymap_layer_name(uint8_t layer) { - if (layer >= ZMK_KEYMAP_LAYERS_LEN) { +const struct zmk_behavior_binding * +zmk_keymap_get_layer_binding_at_idx(zmk_keymap_layer_id_t layer_id, uint8_t binding_idx) { + if (binding_idx >= ZMK_KEYMAP_LEN) { return NULL; } - return zmk_keymap_layer_names[layer]; + ASSERT_LAYER_VAL(layer_id, NULL) + + return &zmk_keymap[layer_id][binding_idx]; +} + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +#define PENDING_ARRAY_SIZE DIV_ROUND_UP(ZMK_KEYMAP_LEN, 8) + +static uint8_t zmk_keymap_layer_pending_changes[ZMK_KEYMAP_LAYERS_LEN][PENDING_ARRAY_SIZE]; + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +int zmk_keymap_set_layer_binding_at_idx(zmk_keymap_layer_id_t layer_id, uint8_t binding_idx, + struct zmk_behavior_binding binding) { + if (binding_idx >= ZMK_KEYMAP_LEN) { + return -EINVAL; + } + + ASSERT_LAYER_VAL(layer_id, -EINVAL) + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + uint8_t *pending = zmk_keymap_layer_pending_changes[layer_id]; + + WRITE_BIT(pending[binding_idx / 8], binding_idx % 8, 1); +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + + // TODO: Need a mutex to protect access to the keymap data? + memcpy(&zmk_keymap[layer_id][binding_idx], &binding, sizeof(binding)); + + return 0; } +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +static uint8_t settings_layer_orders[ZMK_KEYMAP_LAYERS_LEN]; + +#endif + +int zmk_keymap_move_layer(zmk_keymap_layer_index_t start_idx, zmk_keymap_layer_index_t dest_idx) { + ASSERT_LAYER_VAL(start_idx, -EINVAL) + ASSERT_LAYER_VAL(dest_idx, -EINVAL) + + if (start_idx == dest_idx) { + return 0; + } else if (dest_idx > start_idx) { + uint8_t val = keymap_layer_orders[start_idx]; + + for (int i = start_idx; i < dest_idx; i++) { + keymap_layer_orders[i] = keymap_layer_orders[i + 1]; + } + + keymap_layer_orders[dest_idx] = val; + } else { + uint8_t val = keymap_layer_orders[start_idx]; + + for (int i = start_idx; i > dest_idx; i--) { + keymap_layer_orders[i] = keymap_layer_orders[i - 1]; + } + + keymap_layer_orders[dest_idx] = val; + } + + return 0; +} + +int zmk_keymap_add_layer(void) { + uint32_t seen_layer_ids = 0; + LOG_HEXDUMP_DBG(keymap_layer_orders, ZMK_KEYMAP_LAYERS_LEN, "Order"); + + for (int index = 0; index < ZMK_KEYMAP_LAYERS_LEN; index++) { + zmk_keymap_layer_id_t id = LAYER_INDEX_TO_ID(index); + + if (id != ZMK_KEYMAP_LAYER_ID_INVAL) { + WRITE_BIT(seen_layer_ids, id, 1); + continue; + } + + for (int candidate_id = 0; candidate_id < ZMK_KEYMAP_LAYERS_LEN; candidate_id++) { + if (!(seen_layer_ids & BIT(candidate_id))) { + keymap_layer_orders[index] = candidate_id; + return index; + } + } + } + + return -ENOSPC; +} + +int zmk_keymap_remove_layer(zmk_keymap_layer_index_t index) { + ASSERT_LAYER_VAL(index, -EINVAL); + + if (keymap_layer_orders[index] == ZMK_KEYMAP_LAYER_ID_INVAL) { + return -EINVAL; + } + + LOG_DBG("Removing layer index %d which is ID %d", index, keymap_layer_orders[index]); + LOG_HEXDUMP_DBG(keymap_layer_orders, ZMK_KEYMAP_LAYERS_LEN, "Order"); + + while (index < ZMK_KEYMAP_LAYERS_LEN - 1) { + keymap_layer_orders[index] = keymap_layer_orders[index + 1]; + index++; + } + + keymap_layer_orders[ZMK_KEYMAP_LAYERS_LEN - 1] = ZMK_KEYMAP_LAYER_ID_INVAL; + + LOG_HEXDUMP_DBG(keymap_layer_orders, ZMK_KEYMAP_LAYERS_LEN, "Order"); + + return 0; +} + +int zmk_keymap_restore_layer(zmk_keymap_layer_id_t id, zmk_keymap_layer_index_t at_index) { + ASSERT_LAYER_VAL(at_index, -EINVAL); + ASSERT_LAYER_VAL(id, -ENODEV); + + for (zmk_keymap_layer_index_t index = ZMK_KEYMAP_LAYERS_LEN - 1; index > at_index; index--) { + keymap_layer_orders[index] = keymap_layer_orders[index - 1]; + } + + keymap_layer_orders[at_index] = id; + + return 0; +} + +int zmk_keymap_set_layer_name(zmk_keymap_layer_id_t id, const char *name, size_t size) { + ASSERT_LAYER_VAL(id, -EINVAL); + + if (size >= CONFIG_ZMK_KEYMAP_LAYER_NAME_MAX_LEN) { + return -ENOSPC; + } + + strlcpy(zmk_keymap_layer_names[id], name, CONFIG_ZMK_KEYMAP_LAYER_NAME_MAX_LEN); + + // Ensure we properly null terminate our name if we previously had a longer one. + if (size < CONFIG_ZMK_KEYMAP_LAYER_NAME_MAX_LEN - 1) { + zmk_keymap_layer_names[id][size] = 0; + } + + WRITE_BIT(changed_layer_names, id, 1); + + return 0; +} + +#else + +int zmk_keymap_move_layer(zmk_keymap_layer_index_t layer, zmk_keymap_layer_index_t dest) { + return -ENOTSUP; +} + +int zmk_keymap_add_layer(void) { return -ENOTSUP; } + +int zmk_keymap_remove_layer(zmk_keymap_layer_index_t index) { return -ENOTSUP; } + +int zmk_keymap_restore_layer(zmk_keymap_layer_id_t id, zmk_keymap_layer_index_t at_index) { + return -ENOTSUP; +} + +int zmk_keymap_set_layer_name(zmk_keymap_layer_id_t id, const char *name, size_t size) { + return -ENOTSUP; +} + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +#define PENDING_ARRAY_SIZE DIV_ROUND_UP(ZMK_KEYMAP_LEN, 8) + +static uint8_t zmk_keymap_layer_pending_changes[ZMK_KEYMAP_LAYERS_LEN][PENDING_ARRAY_SIZE]; + +struct zmk_behavior_binding_setting { + zmk_behavior_local_id_t behavior_local_id; + uint32_t param1; + uint32_t param2; +} __packed; + +int 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; + } + } + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + if (settings_layer_orders[l] != keymap_layer_orders[l]) { + return 1; + } +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + } + + return 0; +} + +#define LAYER_ORDER_SETTINGS_KEY "keymap/layer_order" +#define LAYER_NAME_SETTINGS_KEY "keymap/l_n/%d" +#define LAYER_BINDING_SETTINGS_KEY "keymap/l/%d/%d" + +static void save_bindings(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)) { + LOG_DBG("Pending save for layer %d at key position %d", l, kp); + + struct zmk_behavior_binding *binding = &zmk_keymap[l][kp]; + struct zmk_behavior_binding_setting binding_setting = { + .behavior_local_id = zmk_behavior_get_local_id(binding->behavior_dev), + .param1 = binding->param1, + .param2 = binding->param2, + }; + + // We can skip any trailing zero params, regardless of the behavior + // and if those params are meaningful. + size_t len = sizeof(binding_setting); + if (binding_setting.param2 == 0) { + len -= 4; + + if (binding_setting.param1 == 0) { + len -= 4; + } + } + + char setting_name[20]; + sprintf(setting_name, LAYER_BINDING_SETTINGS_KEY, l, kp); + + settings_save_one(setting_name, &binding_setting, len); + } + } + + *pending = 0; + } +} + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) +static void save_layer_orders(void) { + settings_save_one(LAYER_ORDER_SETTINGS_KEY, keymap_layer_orders, + ARRAY_SIZE(keymap_layer_orders)); + memcpy(settings_layer_orders, keymap_layer_orders, ARRAY_SIZE(keymap_layer_orders)); +} +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +static void save_layer_names(void) { + for (int id = 0; id < ZMK_KEYMAP_LAYERS_LEN; id++) { + if (changed_layer_names & BIT(id)) { + char setting_name[14]; + sprintf(setting_name, LAYER_NAME_SETTINGS_KEY, id); + settings_save_one(setting_name, zmk_keymap_layer_names[id], + strlen(zmk_keymap_layer_names[id])); + } + } + + changed_layer_names = 0; +} + +int zmk_keymap_save_changes(void) { + save_bindings(); + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + save_layer_orders(); +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + + save_layer_names(); + + return 0; +} + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + +#define KEYMAP_LAYER_ORDER_INIT(n) \ + keymap_layer_orders[i] = i; \ + settings_layer_orders[i] = i; \ + i++; + +static void load_stock_keymap_layer_ordering() { + int i = 0; + DT_INST_FOREACH_CHILD_STATUS_OKAY(0, KEYMAP_LAYER_ORDER_INIT) + while (i < ZMK_KEYMAP_LAYERS_LEN) { + keymap_layer_orders[i] = ZMK_KEYMAP_LAYER_ID_INVAL; + i++; + } +} +#endif + +static void reload_from_stock_keymap(void) { + for (int l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { + for (int k = 0; k < ZMK_KEYMAP_LEN; k++) { + zmk_keymap[l][k] = zmk_stock_keymap[l][k]; + } + } +} + +int zmk_keymap_discard_changes(void) { + load_stock_keymap_layer_ordering(); + reload_from_stock_keymap(); + + int ret = settings_load_subtree("keymap"); + if (ret >= 0) { + changed_layer_names = 0; + + for (int l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { + memset(zmk_keymap_layer_pending_changes[l], 0, PENDING_ARRAY_SIZE); + } + } + + return ret; +} + +int zmk_keymap_reset_settings(void) { + settings_delete(LAYER_ORDER_SETTINGS_KEY); + for (int l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { + char layer_name_setting_name[14]; + sprintf(layer_name_setting_name, LAYER_NAME_SETTINGS_KEY, l); + settings_delete(layer_name_setting_name); + + for (int k = 0; k < ZMK_KEYMAP_LEN; k++) { + if (memcmp(&zmk_keymap[l][k], &zmk_stock_keymap[l][k], + sizeof(struct zmk_behavior_binding_setting)) == 0) { + continue; + } + + char setting_name[20]; + sprintf(setting_name, LAYER_BINDING_SETTINGS_KEY, l, k); + settings_delete(setting_name); + } + } + + load_stock_keymap_layer_ordering(); + + reload_from_stock_keymap(); + + return 0; +} + +#else + +int zmk_keymap_save_changes(void) { return -ENOTSUP; } + +int zmk_keymap_discard_changes(void) { return -ENOTSUP; } + +int zmk_keymap_reset_settings(void) { return -ENOTSUP; } + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + int invoke_locally(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event, bool pressed) { if (pressed) { @@ -172,24 +585,28 @@ int invoke_locally(struct zmk_behavior_binding *binding, struct zmk_behavior_bin } } -int zmk_keymap_apply_position_state(uint8_t source, int layer, uint32_t position, bool pressed, - int64_t timestamp) { +int zmk_keymap_apply_position_state(uint8_t source, zmk_keymap_layer_id_t layer_id, + uint32_t position, bool pressed, int64_t timestamp) { // We want to make a copy of this, since it may be converted from // relative to absolute before being invoked - struct zmk_behavior_binding binding = zmk_keymap[layer][position]; + + ASSERT_LAYER_VAL(layer_id, -EINVAL); + + struct zmk_behavior_binding binding = zmk_keymap[layer_id][position]; const struct device *behavior; struct zmk_behavior_binding_event event = { - .layer = layer, + .layer = layer_id, .position = position, .timestamp = timestamp, }; - LOG_DBG("layer: %d position: %d, binding name: %s", layer, position, binding.behavior_dev); + LOG_DBG("layer_id: %d position: %d, binding name: %s", layer_id, position, + binding.behavior_dev); behavior = zmk_behavior_get_binding(binding.behavior_dev); if (!behavior) { - LOG_WRN("No behavior assigned to %d on layer %d", position, layer); + LOG_WRN("No behavior assigned to %d on layer %d", position, layer_id); return 1; } @@ -236,9 +653,19 @@ int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pr if (pressed) { zmk_keymap_active_behavior_layer[position] = _zmk_keymap_layer_state; } - for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) { - if (zmk_keymap_layer_active_with_state(layer, zmk_keymap_active_behavior_layer[position])) { - int ret = zmk_keymap_apply_position_state(source, layer, position, pressed, timestamp); + + // We use int here to be sure we don't loop layer_idx back to UINT8_MAX + for (int layer_idx = ZMK_KEYMAP_LAYERS_LEN - 1; + layer_idx >= LAYER_ID_TO_INDEX(_zmk_keymap_layer_default); layer_idx--) { + zmk_keymap_layer_id_t layer_id = LAYER_INDEX_TO_ID(layer_idx); + + if (layer_id == ZMK_KEYMAP_LAYER_ID_INVAL) { + continue; + } + if (zmk_keymap_layer_active_with_state(layer_id, + zmk_keymap_active_behavior_layer[position])) { + int ret = + zmk_keymap_apply_position_state(source, layer_id, position, pressed, timestamp); if (ret > 0) { LOG_DBG("behavior processing to continue to next layer"); continue; @@ -260,20 +687,26 @@ int zmk_keymap_sensor_event(uint8_t sensor_index, size_t channel_data_size, int64_t timestamp) { bool opaque_response = false; - for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= 0; layer--) { - struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer][sensor_index]; + for (int layer_idx = ZMK_KEYMAP_LAYERS_LEN - 1; layer_idx >= 0; layer_idx--) { + uint8_t layer_id = LAYER_INDEX_TO_ID(layer_idx); - LOG_DBG("layer: %d sensor_index: %d, binding name: %s", layer, sensor_index, - binding->behavior_dev); + if (layer_id >= ZMK_KEYMAP_LAYERS_LEN) { + continue; + } + + struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer_id][sensor_index]; + + LOG_DBG("layer idx: %d, layer id: %d sensor_index: %d, binding name: %s", layer_idx, + layer_id, sensor_index, binding->behavior_dev); const struct device *behavior = zmk_behavior_get_binding(binding->behavior_dev); if (!behavior) { - LOG_DBG("No behavior assigned to %d on layer %d", sensor_index, layer); + LOG_DBG("No behavior assigned to %d on layer %d", sensor_index, layer_id); continue; } struct zmk_behavior_binding_event event = { - .layer = layer, + .layer = layer_id, .position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_index), .timestamp = timestamp, }; @@ -290,8 +723,8 @@ int zmk_keymap_sensor_event(uint8_t sensor_index, } enum behavior_sensor_binding_process_mode mode = - (!opaque_response && layer >= _zmk_keymap_layer_default && - zmk_keymap_layer_active(layer)) + (!opaque_response && layer_idx >= LAYER_ID_TO_INDEX(_zmk_keymap_layer_default) && + zmk_keymap_layer_active(layer_id)) ? BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER : BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD; @@ -335,3 +768,137 @@ ZMK_SUBSCRIPTION(keymap, zmk_position_state_changed); #if ZMK_KEYMAP_HAS_SENSORS ZMK_SUBSCRIPTION(keymap, zmk_sensor_event); #endif /* ZMK_KEYMAP_HAS_SENSORS */ + +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +static int keymap_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { + const char *next; + + LOG_DBG("Setting Keymap setting %s", name); + + if (settings_name_steq(name, "l_n", &next) && next) { + char *endptr; + zmk_keymap_layer_id_t layer = strtoul(next, &endptr, 10); + + if (*endptr != '\0') { + LOG_WRN("Invalid layer number: %s with endptr %s", next, endptr); + return -EINVAL; + } + + if (layer >= ZMK_KEYMAP_LAYERS_LEN) { + LOG_WRN("Found layer name for invalid layer ID %d", layer); + } + + int err = read_cb(cb_arg, zmk_keymap_layer_names[layer], + MIN(len, CONFIG_ZMK_KEYMAP_LAYER_NAME_MAX_LEN - 1)); + if (err <= 0) { + LOG_ERR("Failed to handle keymap layer name from settings (err %d)", err); + return err; + } + } else if (settings_name_steq(name, "l", &next) && next) { + char *endptr; + uint8_t layer = strtoul(next, &endptr, 10); + if (*endptr != '/') { + LOG_WRN("Invalid layer number: %s with endptr %s", next, endptr); + return -EINVAL; + } + + uint8_t key_position = strtoul(endptr + 1, &endptr, 10); + + if (*endptr != '\0') { + LOG_WRN("Invalid key_position number: %s with endptr %s", next, endptr); + return -EINVAL; + } + + if (len > sizeof(struct zmk_behavior_binding_setting)) { + LOG_ERR("Too large binding setting size (got %d expected %d)", len, + sizeof(struct zmk_behavior_binding_setting)); + return -EINVAL; + } + + if (layer >= ZMK_KEYMAP_LAYERS_LEN) { + LOG_WRN("Layer %d is larger than max of %d", layer, ZMK_KEYMAP_LAYERS_LEN); + return -EINVAL; + } + + if (key_position >= ZMK_KEYMAP_LEN) { + LOG_WRN("Key position %d is larger than max of %d", key_position, ZMK_KEYMAP_LEN); + return -EINVAL; + } + + struct zmk_behavior_binding_setting binding_setting = {0}; + int err = read_cb(cb_arg, &binding_setting, len); + if (err <= 0) { + LOG_ERR("Failed to handle keymap binding from settings (err %d)", err); + return err; + } + + const char *name = + zmk_behavior_find_behavior_name_from_local_id(binding_setting.behavior_local_id); + + if (!name) { + LOG_WRN("Loaded device %d from settings but no device found by that local ID", + binding_setting.behavior_local_id); + } + + zmk_keymap[layer][key_position] = (struct zmk_behavior_binding){ + .local_id = binding_setting.behavior_local_id, + .behavior_dev = name, + .param1 = binding_setting.param1, + .param2 = binding_setting.param2, + }; + } +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + else if (settings_name_steq(name, "layer_order", &next) && !next) { + int err = + read_cb(cb_arg, settings_layer_orders, MIN(len, ARRAY_SIZE(settings_layer_orders))); + if (err <= 0) { + LOG_ERR("Failed to handle keymap layer orders from settings (err %d)", err); + return err; + } + + LOG_HEXDUMP_DBG(settings_layer_orders, ARRAY_SIZE(settings_layer_orders), + "Settings Layer Order"); + + memcpy(keymap_layer_orders, settings_layer_orders, + MIN(len, ARRAY_SIZE(settings_layer_orders))); + } +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + + return 0; +}; + +static int keymap_handle_commit(void) { + for (int l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { + for (int p = 0; p < ZMK_KEYMAP_LEN; p++) { + struct zmk_behavior_binding *binding = &zmk_keymap[l][p]; + + if (binding->local_id > 0 && !binding->behavior_dev) { + binding->behavior_dev = + zmk_behavior_find_behavior_name_from_local_id(binding->local_id); + + if (!binding->behavior_dev) { + LOG_ERR("Failed to finding device for local ID %d after settings load", + binding->local_id); + } + } + } + } + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(keymap, "keymap", NULL, keymap_handle_set, keymap_handle_commit, + NULL); + +#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) + +int keymap_init(void) { +#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) + load_stock_keymap_layer_ordering(); +#endif + + return 0; +} + +SYS_INIT(keymap_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/app/src/physical_layouts.c b/app/src/physical_layouts.c index 16b13e710c..00cfa29e78 100644 --- a/app/src/physical_layouts.c +++ b/app/src/physical_layouts.c @@ -72,9 +72,17 @@ struct position_map_entry { const uint32_t positions[ZMK_POS_MAP_LEN]; }; +#define ZMK_POS_MAP_LEN_CHECK(node_id) \ + BUILD_ASSERT(ZMK_POS_MAP_LEN == DT_PROP_LEN(node_id, positions), \ + "Position maps must all have the same number of entries") + +DT_FOREACH_CHILD_SEP(DT_INST(0, POS_MAP_COMPAT), ZMK_POS_MAP_LEN_CHECK, (;)); + #define ZMK_POS_MAP_ENTRY(node_id) \ { \ - .layout = &_CONCAT(_zmk_physical_layout_, DT_PHANDLE(node_id, physical_layout)), \ + .layout = COND_CODE_1( \ + DT_HAS_COMPAT_STATUS_OKAY(DT_PHANDLE(node_id, physical_layout)), \ + (&_CONCAT(_zmk_physical_layout_, DT_PHANDLE(node_id, physical_layout))), (NULL)), \ .positions = DT_PROP(node_id, positions), \ } @@ -275,13 +283,15 @@ int zmk_physical_layouts_save_selected(void) { int zmk_physical_layouts_revert_selected(void) { return zmk_physical_layouts_select_initial(); } -int zmk_physical_layouts_get_position_map(uint8_t source, uint8_t dest, uint32_t *map) { +int zmk_physical_layouts_get_position_map(uint8_t source, uint8_t dest, size_t map_size, + uint32_t map[map_size]) { if (source >= ARRAY_SIZE(layouts) || dest >= ARRAY_SIZE(layouts)) { return -EINVAL; } const struct zmk_physical_layout *src_layout = layouts[source]; const struct zmk_physical_layout *dest_layout = layouts[dest]; + int max_kp = dest_layout->keys_len; #if HAVE_POS_MAP const struct position_map_entry *src_pos_map = NULL; @@ -296,11 +306,24 @@ int zmk_physical_layouts_get_position_map(uint8_t source, uint8_t dest, uint32_t dest_pos_map = &positions_maps[pm]; } } + + // Maps can place items "off the end" of other layouts so they are + // preserved but not visible, so adjust our max here if that is being used. + if (src_pos_map && dest_pos_map) { + for (int mp = 0; mp < ZMK_POS_MAP_LEN; mp++) { + max_kp = + MAX(max_kp, MAX(src_pos_map->positions[mp] + 1, dest_pos_map->positions[mp] + 1)); + } + } #endif - memset(map, UINT32_MAX, dest_layout->keys_len); + if (map_size < max_kp) { + return -EINVAL; + } + + memset(map, UINT32_MAX, map_size); - for (int b = 0; b < dest_layout->keys_len; b++) { + for (int b = 0; b < max_kp; b++) { bool found = false; #if HAVE_POS_MAP @@ -329,13 +352,9 @@ int zmk_physical_layouts_get_position_map(uint8_t source, uint8_t dest, uint32_t } } #endif - - if (!found || map[b] >= src_layout->keys_len) { - map[b] = UINT32_MAX; - } } - return dest_layout->keys_len; + return max_kp; } #if IS_ENABLED(CONFIG_SETTINGS) diff --git a/app/src/studio/CMakeLists.txt b/app/src/studio/CMakeLists.txt index e8f0d49d26..a8417dd768 100644 --- a/app/src/studio/CMakeLists.txt +++ b/app/src/studio/CMakeLists.txt @@ -3,6 +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-event-mappers.ld) zephyr_linker_sources(SECTIONS ../../include/linker/zmk-rpc-transport.ld) @@ -11,5 +12,6 @@ target_sources(app PRIVATE rpc.c) target_sources(app PRIVATE core.c) target_sources(app PRIVATE behavior_subsystem.c) target_sources(app PRIVATE core_subsystem.c) +target_sources(app PRIVATE keymap_subsystem.c) target_sources_ifdef(CONFIG_ZMK_STUDIO_TRANSPORT_UART app PRIVATE uart_rpc_transport.c) target_sources_ifdef(CONFIG_ZMK_STUDIO_TRANSPORT_BLE app PRIVATE gatt_rpc_transport.c)
\ No newline at end of file diff --git a/app/src/studio/Kconfig b/app/src/studio/Kconfig index ebe680bb8c..d1315070e6 100644 --- a/app/src/studio/Kconfig +++ b/app/src/studio/Kconfig @@ -41,6 +41,8 @@ menuconfig ZMK_STUDIO_RPC select ZMK_BEHAVIOR_METADATA select ZMK_BEHAVIOR_LOCAL_IDS select RING_BUFFER + select ZMK_KEYMAP_SETTINGS_STORAGE + select ZMK_KEYMAP_LAYER_REORDERING help Add firmware support for studio RPC protocol diff --git a/app/src/studio/core_subsystem.c b/app/src/studio/core_subsystem.c index 001aed9b9c..2cdc9d7ce4 100644 --- a/app/src/studio/core_subsystem.c +++ b/app/src/studio/core_subsystem.c @@ -61,8 +61,21 @@ zmk_studio_Response get_lock_state(const zmk_studio_Request *req) { return CORE_RESPONSE(get_lock_state, resp); } +zmk_studio_Response reset_settings(const zmk_studio_Request *req) { + ZMK_RPC_SUBSYSTEM_SETTINGS_RESET_FOREACH(sub) { + int ret = sub->callback(); + if (ret < 0) { + LOG_ERR("Failed to reset settings: %d", ret); + return CORE_RESPONSE(reset_settings, false); + } + } + + return CORE_RESPONSE(reset_settings, true); +} + 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); 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 new file mode 100644 index 0000000000..aa4b979920 --- /dev/null +++ b/app/src/studio/keymap_subsystem.c @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include <zephyr/logging/log.h> + +LOG_MODULE_DECLARE(zmk_studio, CONFIG_ZMK_STUDIO_LOG_LEVEL); + +#include <drivers/behavior.h> + +#include <zmk/behavior.h> +#include <zmk/matrix.h> +#include <zmk/keymap.h> +#include <zmk/studio/rpc.h> +#include <zmk/physical_layouts.h> + +#include <pb_encode.h> + +ZMK_RPC_SUBSYSTEM(keymap) + +#define KEYMAP_RESPONSE(type, ...) ZMK_RPC_RESPONSE(keymap, type, __VA_ARGS__) +#define KEYMAP_NOTIFICATION(type, ...) ZMK_RPC_NOTIFICATION(keymap, type, __VA_ARGS__) + +static bool encode_layer_bindings(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { + const zmk_keymap_layer_id_t layer_id = *(uint8_t *)*arg; + + for (int b = 0; b < ZMK_KEYMAP_LEN; b++) { + const struct zmk_behavior_binding *binding = + zmk_keymap_get_layer_binding_at_idx(layer_id, b); + + zmk_keymap_BehaviorBinding bb = zmk_keymap_BehaviorBinding_init_zero; + + if (binding && binding->behavior_dev) { + bb.behavior_id = zmk_behavior_get_local_id(binding->behavior_dev); + bb.param1 = binding->param1; + bb.param2 = binding->param2; + } + + if (!pb_encode_tag_for_field(stream, field)) { + return false; + } + + if (!pb_encode_submessage(stream, &zmk_keymap_BehaviorBinding_msg, &bb)) { + return false; + } + } + + return true; +} + +static bool encode_layer_name(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { + const zmk_keymap_layer_index_t layer_idx = *(uint8_t *)*arg; + + const char *name = zmk_keymap_layer_name(layer_idx); + + if (!name) { + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) { + return false; + } + + return pb_encode_string(stream, name, strlen(name)); +} + +static bool encode_keymap_layers(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { + for (zmk_keymap_layer_index_t l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { + zmk_keymap_layer_id_t layer_id = zmk_keymap_layer_index_to_id(l); + + if (layer_id == UINT8_MAX) { + break; + } + + if (!pb_encode_tag_for_field(stream, field)) { + LOG_WRN("Failed to encode tag"); + return false; + } + + zmk_keymap_Layer layer = zmk_keymap_Layer_init_zero; + layer.id = layer_id; + + layer.name.funcs.encode = encode_layer_name; + layer.name.arg = &layer_id; + + layer.bindings.funcs.encode = encode_layer_bindings; + layer.bindings.arg = &layer_id; + + if (!pb_encode_submessage(stream, &zmk_keymap_Layer_msg, &layer)) { + LOG_WRN("Failed to encode layer submessage"); + return false; + } + } + + return true; +} + +zmk_studio_Response get_keymap(const zmk_studio_Request *req) { + zmk_keymap_Keymap resp = zmk_keymap_Keymap_init_zero; + + resp.layers.funcs.encode = encode_keymap_layers; + + resp.available_layers = 0; + + for (zmk_keymap_layer_index_t index = 0; index < ZMK_KEYMAP_LAYERS_LEN; index++) { + zmk_keymap_layer_id_t id = zmk_keymap_layer_index_to_id(index); + + if (id == UINT8_MAX) { + resp.available_layers = ZMK_KEYMAP_LAYERS_LEN - index; + break; + } + } + + return KEYMAP_RESPONSE(get_keymap, resp); +} + +zmk_studio_Response set_layer_binding(const zmk_studio_Request *req) { + const zmk_keymap_SetLayerBindingRequest *set_req = + &req->subsystem.keymap.request_type.set_layer_binding; + + zmk_behavior_local_id_t bid = set_req->binding.behavior_id; + + const char *behavior_name = zmk_behavior_find_behavior_name_from_local_id(bid); + + if (!behavior_name) { + return KEYMAP_RESPONSE( + set_layer_binding, + zmk_keymap_SetLayerBindingResponse_SET_LAYER_BINDING_RESP_INVALID_BEHAVIOR); + } + + struct zmk_behavior_binding binding = (struct zmk_behavior_binding){ + .behavior_dev = behavior_name, + .param1 = set_req->binding.param1, + .param2 = set_req->binding.param2, + }; + + int ret = zmk_behavior_validate_binding(&binding); + if (ret < 0) { + return KEYMAP_RESPONSE( + set_layer_binding, + zmk_keymap_SetLayerBindingResponse_SET_LAYER_BINDING_RESP_INVALID_PARAMETERS); + } + + ret = zmk_keymap_set_layer_binding_at_idx(set_req->layer_id, set_req->key_position, binding); + + if (ret < 0) { + LOG_WRN("Setting the binding failed with %d", ret); + switch (ret) { + case -EINVAL: + return KEYMAP_RESPONSE( + set_layer_binding, + zmk_keymap_SetLayerBindingResponse_SET_LAYER_BINDING_RESP_INVALID_LOCATION); + default: + return ZMK_RPC_SIMPLE_ERR(GENERIC); + } + } + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + + return KEYMAP_RESPONSE(set_layer_binding, + zmk_keymap_SetLayerBindingResponse_SET_LAYER_BINDING_RESP_OK); +} + +zmk_studio_Response check_unsaved_changes(const zmk_studio_Request *req) { + int layout_changes = zmk_physical_layouts_check_unsaved_selection(); + int keymap_changes = zmk_keymap_check_unsaved_changes(); + + return KEYMAP_RESPONSE(check_unsaved_changes, layout_changes > 0 || keymap_changes > 0); +} + +zmk_studio_Response save_changes(const zmk_studio_Request *req) { + int ret = zmk_physical_layouts_save_selected(); + + if (ret < 0) { + return ZMK_RPC_SIMPLE_ERR(GENERIC); + } + + ret = zmk_keymap_save_changes(); + if (ret < 0) { + return ZMK_RPC_SIMPLE_ERR(GENERIC); + } + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, false)}); + + return KEYMAP_RESPONSE(save_changes, true); +} + +zmk_studio_Response discard_changes(const zmk_studio_Request *req) { + int ret = zmk_physical_layouts_revert_selected(); + if (ret < 0) { + return ZMK_RPC_SIMPLE_ERR(GENERIC); + } + + ret = zmk_keymap_discard_changes(); + if (ret < 0) { + return ZMK_RPC_SIMPLE_ERR(GENERIC); + } + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, false)}); + + return KEYMAP_RESPONSE(discard_changes, true); +} + +static int keymap_settings_reset(void) { return zmk_keymap_reset_settings(); } + +ZMK_RPC_SUBSYSTEM_SETTINGS_RESET(keymap, keymap_settings_reset); + +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; + + if (!layout->display_name) { + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) { + LOG_WRN("Failed to encode tag"); + return false; + } + + pb_encode_string(stream, layout->display_name, strlen(layout->display_name)); + + return true; +} + +static bool encode_layout_keys(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { + struct zmk_physical_layout *layout = (struct zmk_physical_layout *)*arg; + + for (int kp = 0; kp < layout->keys_len; kp++) { + const struct zmk_key_physical_attrs *layout_kp = &layout->keys[kp]; + + if (!pb_encode_tag_for_field(stream, field)) { + LOG_WRN("Failed to encode tag"); + return false; + } + + zmk_keymap_KeyPhysicalAttrs layout_kp_msg = { + .width = layout_kp->width, + .height = layout_kp->height, + .x = layout_kp->x, + .y = layout_kp->y, + .r = layout_kp->r, + .rx = layout_kp->rx, + .ry = layout_kp->ry, + }; + + if (!pb_encode_submessage(stream, &zmk_keymap_KeyPhysicalAttrs_msg, &layout_kp_msg)) { + LOG_WRN("Failed to encode layout key position submessage"); + return false; + } + } + return true; +} + +static bool encode_layouts(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { + struct zmk_physical_layout const *const *layouts; + const size_t layout_count = zmk_physical_layouts_get_list(&layouts); + + for (int i = 0; i < layout_count; i++) { + const struct zmk_physical_layout *l = layouts[i]; + + if (!pb_encode_tag_for_field(stream, field)) { + LOG_WRN("Failed to encode tag"); + return false; + } + + zmk_keymap_PhysicalLayout layout = zmk_keymap_PhysicalLayout_init_zero; + + layout.name.funcs.encode = encode_layout_name; + layout.name.arg = l; + + layout.keys.funcs.encode = encode_layout_keys; + layout.keys.arg = l; + + if (!pb_encode_submessage(stream, &zmk_keymap_PhysicalLayout_msg, &layout)) { + LOG_WRN("Failed to encode layout submessage"); + return false; + } + } + + return true; +} + +zmk_studio_Response get_physical_layouts(const zmk_studio_Request *req) { + zmk_keymap_PhysicalLayouts resp = zmk_keymap_PhysicalLayouts_init_zero; + resp.active_layout_index = zmk_physical_layouts_get_selected(); + resp.layouts.funcs.encode = encode_layouts; + return KEYMAP_RESPONSE(get_physical_layouts, resp); +} + +static void migrate_keymap(const uint8_t old) { + int new = zmk_physical_layouts_get_selected(); + + uint32_t new_to_old_map[ZMK_KEYMAP_LEN]; + int layout_size = + zmk_physical_layouts_get_position_map(old, new, ZMK_KEYMAP_LEN, new_to_old_map); + + if (layout_size < 0) { + return; + } + + for (int l = 0; l < ZMK_KEYMAP_LAYERS_LEN; l++) { + struct zmk_behavior_binding new_layer[ZMK_KEYMAP_LEN]; + + for (int b = 0; b < layout_size; b++) { + uint32_t old_b = new_to_old_map[b]; + + if (old_b == UINT32_MAX) { + memset(&new_layer[b], 0, sizeof(struct zmk_behavior_binding)); + continue; + } + + const struct zmk_behavior_binding *binding = + zmk_keymap_get_layer_binding_at_idx(l, old_b); + + if (!binding) { + memset(&new_layer[b], 0, sizeof(struct zmk_behavior_binding)); + continue; + } + + memcpy(&new_layer[b], binding, sizeof(struct zmk_behavior_binding)); + } + + for (int b = 0; b < layout_size; b++) { + zmk_keymap_set_layer_binding_at_idx(l, b, new_layer[b]); + } + } + + // TODO: Migrate combos? +} + +zmk_studio_Response set_active_physical_layout(const zmk_studio_Request *req) { + uint8_t index = (uint8_t)req->subsystem.keymap.request_type.set_active_physical_layout; + int old = zmk_physical_layouts_get_selected(); + + zmk_keymap_SetActivePhysicalLayoutResponse resp = + zmk_keymap_SetActivePhysicalLayoutResponse_init_zero; + resp.which_result = zmk_keymap_SetActivePhysicalLayoutResponse_ok_tag; + resp.result.ok.layers.funcs.encode = encode_keymap_layers; + + if (old == index) { + return KEYMAP_RESPONSE(set_active_physical_layout, resp); + } + + int ret = zmk_physical_layouts_select(index); + if (ret >= 0) { + migrate_keymap(old); + } else { + resp.which_result = zmk_keymap_SetActivePhysicalLayoutResponse_err_tag; + resp.result.err = + zmk_keymap_SetActivePhysicalLayoutErrorCode_SET_ACTIVE_PHYSICAL_LAYOUT_ERR_GENERIC; + } + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + + return KEYMAP_RESPONSE(set_active_physical_layout, resp); +} + +zmk_studio_Response move_layer(const zmk_studio_Request *req) { + const zmk_keymap_MoveLayerRequest *move_req = &req->subsystem.keymap.request_type.move_layer; + + zmk_keymap_MoveLayerResponse resp = zmk_keymap_MoveLayerResponse_init_zero; + + int ret = zmk_keymap_move_layer(move_req->start_index, move_req->dest_index); + + if (ret >= 0) { + resp.which_result = zmk_keymap_SetActivePhysicalLayoutResponse_ok_tag; + resp.result.ok.layers.funcs.encode = encode_keymap_layers; + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + } else { + LOG_WRN("Failed to move layer: %d", ret); + resp.which_result = zmk_keymap_MoveLayerResponse_err_tag; + resp.result.err = zmk_keymap_MoveLayerErrorCode_MOVE_LAYER_ERR_GENERIC; + } + + return KEYMAP_RESPONSE(move_layer, resp); +} + +zmk_studio_Response add_layer(const zmk_studio_Request *req) { + // Use a static here to keep the value valid during serialization + static zmk_keymap_layer_id_t layer_id = 0; + + zmk_keymap_AddLayerResponse resp = zmk_keymap_AddLayerResponse_init_zero; + + int ret = zmk_keymap_add_layer(); + + if (ret >= 0) { + layer_id = zmk_keymap_layer_index_to_id(ret); + + resp.which_result = zmk_keymap_AddLayerResponse_ok_tag; + + resp.result.ok.index = ret; + + resp.result.ok.has_layer = true; + resp.result.ok.layer.id = layer_id; + + resp.result.ok.layer.name.funcs.encode = encode_layer_name; + resp.result.ok.layer.name.arg = &layer_id; + + resp.result.ok.layer.bindings.funcs.encode = encode_layer_bindings; + resp.result.ok.layer.bindings.arg = &layer_id; + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + } else { + LOG_WRN("Failed to add layer: %d", ret); + resp.which_result = zmk_keymap_AddLayerResponse_err_tag; + switch (ret) { + case -ENOSPC: + resp.result.err = zmk_keymap_AddLayerErrorCode_ADD_LAYER_ERR_NO_SPACE; + break; + default: + resp.result.err = zmk_keymap_AddLayerErrorCode_ADD_LAYER_ERR_GENERIC; + break; + } + } + + return KEYMAP_RESPONSE(add_layer, resp); +} + +zmk_studio_Response remove_layer(const zmk_studio_Request *req) { + const zmk_keymap_RemoveLayerRequest *rm_req = &req->subsystem.keymap.request_type.remove_layer; + + zmk_keymap_RemoveLayerResponse resp = zmk_keymap_RemoveLayerResponse_init_zero; + + int ret = zmk_keymap_remove_layer(rm_req->layer_index); + + if (ret >= 0) { + resp.which_result = zmk_keymap_RemoveLayerResponse_ok_tag; + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + } else { + LOG_WRN("Failed to rm layer: %d", ret); + resp.which_result = zmk_keymap_RemoveLayerResponse_err_tag; + switch (ret) { + case -EINVAL: + resp.result.err = zmk_keymap_RemoveLayerErrorCode_REMOVE_LAYER_ERR_INVALID_INDEX; + break; + default: + resp.result.err = zmk_keymap_RemoveLayerErrorCode_REMOVE_LAYER_ERR_GENERIC; + break; + } + } + + return KEYMAP_RESPONSE(remove_layer, resp); +} + +zmk_studio_Response restore_layer(const zmk_studio_Request *req) { + const zmk_keymap_RestoreLayerRequest *restore_req = + &req->subsystem.keymap.request_type.restore_layer; + + zmk_keymap_RestoreLayerResponse resp = zmk_keymap_RestoreLayerResponse_init_zero; + + int ret = zmk_keymap_restore_layer(restore_req->layer_id, restore_req->at_index); + + if (ret >= 0) { + resp.which_result = zmk_keymap_RemoveLayerResponse_ok_tag; + resp.result.ok.id = restore_req->layer_id; + + resp.result.ok.name.funcs.encode = encode_layer_name; + resp.result.ok.name.arg = &restore_req->layer_id; + + resp.result.ok.bindings.funcs.encode = encode_layer_bindings; + resp.result.ok.bindings.arg = &restore_req->layer_id; + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + } else { + LOG_WRN("Failed to restore layer: %d", ret); + resp.which_result = zmk_keymap_RestoreLayerResponse_err_tag; + switch (ret) { + case -EINVAL: + resp.result.err = zmk_keymap_RestoreLayerErrorCode_RESTORE_LAYER_ERR_INVALID_INDEX; + break; + default: + resp.result.err = zmk_keymap_RestoreLayerErrorCode_RESTORE_LAYER_ERR_GENERIC; + break; + } + } + + return KEYMAP_RESPONSE(restore_layer, resp); +} + +zmk_studio_Response set_layer_props(const zmk_studio_Request *req) { + const zmk_keymap_SetLayerPropsRequest *set_req = + &req->subsystem.keymap.request_type.set_layer_props; + + zmk_keymap_SetLayerPropsResponse resp = + zmk_keymap_SetLayerPropsResponse_SET_LAYER_PROPS_RESP_OK; + + if (strlen(set_req->name) <= 0) { + return KEYMAP_RESPONSE(set_layer_props, resp); + } + + int ret = zmk_keymap_set_layer_name(set_req->layer_id, set_req->name, strlen(set_req->name)); + + if (ret >= 0) { + + raise_zmk_studio_rpc_notification((struct zmk_studio_rpc_notification){ + .notification = KEYMAP_NOTIFICATION(unsaved_changes_status_changed, true)}); + } else { + LOG_WRN("Failed to set layer props: %d", ret); + switch (ret) { + case -EINVAL: + resp = zmk_keymap_SetLayerPropsResponse_SET_LAYER_PROPS_RESP_ERR_INVALID_ID; + break; + default: + resp = zmk_keymap_SetLayerPropsResponse_SET_LAYER_PROPS_RESP_ERR_GENERIC; + break; + } + } + + return KEYMAP_RESPONSE(set_layer_props, resp); +} + +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, get_keymap, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, set_layer_binding, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, check_unsaved_changes, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, save_changes, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, discard_changes, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, get_physical_layouts, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, set_active_physical_layout, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, move_layer, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, add_layer, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, remove_layer, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, restore_layer, ZMK_STUDIO_RPC_HANDLER_SECURED); +ZMK_RPC_SUBSYSTEM_HANDLER(keymap, set_layer_props, ZMK_STUDIO_RPC_HANDLER_SECURED); + +static int event_mapper(const zmk_event_t *eh, zmk_studio_Notification *n) { return 0; } + +ZMK_RPC_EVENT_MAPPER(keymap, event_mapper); |