Files
zmk/app/src/studio/behavior_subsystem.c
2024-09-25 17:46:50 -06:00

214 lines
7.5 KiB
C

/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <pb_encode.h>
#include <zmk/studio/rpc.h>
#include <drivers/behavior.h>
#include <zmk/hid.h>
ZMK_RPC_SUBSYSTEM(behaviors)
#define BEHAVIOR_RESPONSE(type, ...) ZMK_RPC_RESPONSE(behaviors, type, __VA_ARGS__)
static bool encode_behavior_summaries(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg) {
STRUCT_SECTION_FOREACH(zmk_behavior_local_id_map, beh) {
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
if (!pb_encode_varint(stream, beh->local_id)) {
LOG_ERR("Failed to encode behavior ID");
return false;
}
}
return true;
}
zmk_studio_Response list_all_behaviors(const zmk_studio_Request *req) {
LOG_DBG("");
zmk_behaviors_ListAllBehaviorsResponse beh_resp =
zmk_behaviors_ListAllBehaviorsResponse_init_zero;
beh_resp.behaviors.funcs.encode = encode_behavior_summaries;
return BEHAVIOR_RESPONSE(list_all_behaviors, beh_resp);
}
struct encode_metadata_sets_state {
const struct behavior_parameter_metadata_set *sets;
size_t sets_len;
size_t i;
};
static bool encode_value_description_name(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg) {
struct behavior_parameter_value_metadata *state =
(struct behavior_parameter_value_metadata *)*arg;
if (!state->display_name) {
return true;
}
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
return pb_encode_string(stream, state->display_name, strlen(state->display_name));
}
static bool encode_value_description(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg) {
struct encode_metadata_sets_state *state = (struct encode_metadata_sets_state *)*arg;
const struct behavior_parameter_metadata_set *set = &state->sets[state->i];
bool is_param1 = field->tag == zmk_behaviors_BehaviorBindingParametersSet_param1_tag;
size_t values_len = is_param1 ? set->param1_values_len : set->param2_values_len;
const struct behavior_parameter_value_metadata *values =
is_param1 ? set->param1_values : set->param2_values;
for (int val_i = 0; val_i < values_len; val_i++) {
const struct behavior_parameter_value_metadata *val = &values[val_i];
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
zmk_behaviors_BehaviorParameterValueDescription desc =
zmk_behaviors_BehaviorParameterValueDescription_init_zero;
desc.name.funcs.encode = encode_value_description_name;
desc.name.arg = val;
switch (val->type) {
case BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE:
desc.which_value_type = zmk_behaviors_BehaviorParameterValueDescription_constant_tag;
desc.value_type.constant = val->value;
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE:
desc.which_value_type = zmk_behaviors_BehaviorParameterValueDescription_range_tag;
desc.value_type.range.min = val->range.min;
desc.value_type.range.max = val->range.max;
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_NIL:
desc.which_value_type = zmk_behaviors_BehaviorParameterValueDescription_nil_tag;
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE:
desc.which_value_type = zmk_behaviors_BehaviorParameterValueDescription_hid_usage_tag;
desc.value_type.hid_usage.consumer_max = ZMK_HID_CONSUMER_MAX_USAGE;
desc.value_type.hid_usage.keyboard_max = ZMK_HID_KEYBOARD_MAX_USAGE;
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_ID:
desc.which_value_type = zmk_behaviors_BehaviorParameterValueDescription_layer_id_tag;
break;
default:
LOG_ERR("Unknown value description type %d", val->type);
return false;
}
if (!pb_encode_submessage(stream, &zmk_behaviors_BehaviorParameterValueDescription_msg,
&desc)) {
LOG_WRN("Failed to encode submessage for set %d, value %d!", state->i, val_i);
return false;
}
}
return true;
}
static bool encode_metadata_sets(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) {
struct encode_metadata_sets_state *state = (struct encode_metadata_sets_state *)*arg;
bool ret = true;
LOG_DBG("Encoding the %d metadata sets with %p", state->sets_len, state->sets);
for (int i = 0; i < state->sets_len; i++) {
LOG_DBG("Encoding set %d", i);
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
state->i = i;
zmk_behaviors_BehaviorBindingParametersSet msg =
zmk_behaviors_BehaviorBindingParametersSet_init_zero;
msg.param1.funcs.encode = encode_value_description;
msg.param1.arg = state;
msg.param2.funcs.encode = encode_value_description;
msg.param2.arg = state;
ret = pb_encode_submessage(stream, &zmk_behaviors_BehaviorBindingParametersSet_msg, &msg);
if (!ret) {
LOG_WRN("Failed to encode submessage for set %d", i);
break;
}
}
return ret;
}
static bool encode_behavior_name(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) {
struct zmk_behavior_ref *zbm = (struct zmk_behavior_ref *)*arg;
if (!pb_encode_tag_for_field(stream, field)) {
return false;
}
return pb_encode_string(stream, zbm->metadata.display_name, strlen(zbm->metadata.display_name));
}
static struct encode_metadata_sets_state state = {};
zmk_studio_Response get_behavior_details(const zmk_studio_Request *req) {
uint32_t behavior_id = req->subsystem.behaviors.request_type.get_behavior_details.behavior_id;
const char *behavior_name = zmk_behavior_find_behavior_name_from_local_id(behavior_id);
LOG_DBG("behavior_id %d, name %s", behavior_id, behavior_name);
if (!behavior_name) {
LOG_WRN("No behavior found for ID %d", behavior_id);
return ZMK_RPC_SIMPLE_ERR(GENERIC);
}
const struct device *device = behavior_get_binding(behavior_name);
struct zmk_behavior_ref *zbm = NULL;
STRUCT_SECTION_FOREACH(zmk_behavior_ref, item) {
if (item->device == device) {
zbm = item;
break;
}
}
__ASSERT(zbm != NULL, "Can't find a device without also having metadata");
struct behavior_parameter_metadata desc = {0};
int ret = behavior_get_parameter_metadata(device, &desc);
if (ret < 0) {
LOG_DBG("Failed to fetch the metadata for %s! %d", zbm->metadata.display_name, ret);
} else {
LOG_DBG("Got metadata with %d sets", desc.sets_len);
}
zmk_behaviors_GetBehaviorDetailsResponse resp =
zmk_behaviors_GetBehaviorDetailsResponse_init_zero;
resp.id = behavior_id;
resp.display_name.funcs.encode = encode_behavior_name;
resp.display_name.arg = zbm;
state.sets = desc.sets;
state.sets_len = desc.sets_len;
resp.metadata.funcs.encode = encode_metadata_sets;
resp.metadata.arg = &state;
return BEHAVIOR_RESPONSE(get_behavior_details, resp);
}
ZMK_RPC_SUBSYSTEM_HANDLER(behaviors, list_all_behaviors, ZMK_STUDIO_RPC_HANDLER_UNSECURED);
ZMK_RPC_SUBSYSTEM_HANDLER(behaviors, get_behavior_details, ZMK_STUDIO_RPC_HANDLER_SECURED);