/* -*- Mode: C ; c-basic-offset: 4 -*- */ /* Copyright (C) 2011 Nedko Arnaudov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Parameter addresses: * * "engine" * "engine", "driver" * "engine", "realtime" * "engine", ...more engine parameters * * "driver", "device" * "driver", ...more driver parameters * * "drivers", "alsa", "device" * "drivers", "alsa", ...more alsa driver parameters * * "drivers", ...more drivers * * "internals", "netmanager", "multicast_ip" * "internals", "netmanager", ...more netmanager parameters * * "internals", ...more internals * */ #include #include #include #include #include "params.h" #include "controller_internal.h" #define PTNODE_ENGINE "engine" #define PTNODE_DRIVER "driver" #define PTNODE_DRIVERS "drivers" #define PTNODE_INTERNALS "internals" struct jack_parameter_container { struct list_head siblings; char * name; struct jack_parameter_container * symlink; bool leaf; struct list_head children; void * obj; }; struct jack_params { jackctl_server_t * server; struct jack_parameter_container root; struct list_head * drivers_ptr; uint32_t drivers_count; struct jack_parameter_container * driver_ptr; bool driver_set; /* whether driver is manually set, if false - DEFAULT_DRIVER is auto set */ }; static bool controlapi_parameter_is_set(void * obj) { return jackctl_parameter_is_set((jackctl_parameter_t *)obj); } static bool controlapi_parameter_reset(void * obj) { return jackctl_parameter_reset((jackctl_parameter_t *)obj); } union jackctl_parameter_value controlapi_parameter_get_value(void * obj) { return jackctl_parameter_get_value((jackctl_parameter_t *)obj); } bool controlapi_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) { return jackctl_parameter_set_value((jackctl_parameter_t *)obj, value_ptr); } union jackctl_parameter_value controlapi_parameter_get_default_value(void * obj) { return jackctl_parameter_get_default_value((jackctl_parameter_t *)obj); } static struct jack_parameter_container * create_container(struct list_head * parent_list_ptr, const char * name) { struct jack_parameter_container * container_ptr; container_ptr = malloc(sizeof(struct jack_parameter_container)); if (container_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_parameter_container"); goto fail; } container_ptr->name = strdup(name); if (container_ptr->name == NULL) { jack_error("Ran out of memory trying to strdup parameter container name"); goto free; } container_ptr->leaf = false; container_ptr->symlink = NULL; container_ptr->obj = NULL; INIT_LIST_HEAD(&container_ptr->children); list_add_tail(&container_ptr->siblings, parent_list_ptr); return container_ptr; free: free(container_ptr); fail: return NULL; } static bool add_controlapi_param(struct list_head * parent_list_ptr, jackctl_parameter_t * param) { struct jack_parameter * param_ptr; uint32_t i; param_ptr = malloc(sizeof(struct jack_parameter)); if (param_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_parameter"); goto fail; } param_ptr->ext = false; param_ptr->obj = param; param_ptr->vtable.is_set = controlapi_parameter_is_set; param_ptr->vtable.reset = controlapi_parameter_reset; param_ptr->vtable.get_value = controlapi_parameter_get_value; param_ptr->vtable.set_value = controlapi_parameter_set_value; param_ptr->vtable.get_default_value = controlapi_parameter_get_default_value; param_ptr->type = jackctl_parameter_get_type(param); param_ptr->name = jackctl_parameter_get_name(param); param_ptr->short_decr = jackctl_parameter_get_short_description(param); param_ptr->long_descr = jackctl_parameter_get_long_description(param); if (jackctl_parameter_has_range_constraint(param)) { param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; param_ptr->constraint_range = true; jackctl_parameter_get_range_constraint(param, ¶m_ptr->constraint.range.min, ¶m_ptr->constraint.range.max); } else if (jackctl_parameter_has_enum_constraint(param)) { param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID; param_ptr->constraint_range = false; param_ptr->constraint.enumeration.count = jackctl_parameter_get_enum_constraints_count(param); param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * param_ptr->constraint.enumeration.count); if (param_ptr->constraint.enumeration.possible_values_array == NULL) { goto free; } for (i = 0; i < param_ptr->constraint.enumeration.count; i++) { param_ptr->constraint.enumeration.possible_values_array[i].value = jackctl_parameter_get_enum_constraint_value(param, i); param_ptr->constraint.enumeration.possible_values_array[i].short_desc = jackctl_parameter_get_enum_constraint_description(param, i); } } else { param_ptr->constraint_flags = 0; goto add; } if (jackctl_parameter_constraint_is_strict(param)) { param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_STRICT; } if (jackctl_parameter_constraint_is_fake_value(param)) { param_ptr->constraint_flags |= JACK_CONSTRAINT_FLAG_FAKE_VALUE; } add: list_add_tail(¶m_ptr->siblings, parent_list_ptr); return true; free: free(param_ptr); fail: return false; } static void free_params(struct list_head * parent_list_ptr) { struct jack_parameter * param_ptr; while (!list_empty(parent_list_ptr)) { param_ptr = list_entry(parent_list_ptr->next, struct jack_parameter, siblings); list_del(¶m_ptr->siblings); if (param_ptr->ext) { continue; } if ((param_ptr->constraint_flags & JACK_CONSTRAINT_FLAG_VALID) != 0 && !param_ptr->constraint_range && param_ptr->constraint.enumeration.possible_values_array != NULL) { free(param_ptr->constraint.enumeration.possible_values_array); } free(param_ptr); } } static void free_containers(struct list_head * parent_list_ptr) { struct jack_parameter_container * container_ptr; while (!list_empty(parent_list_ptr)) { container_ptr = list_entry(parent_list_ptr->next, struct jack_parameter_container, siblings); list_del(&container_ptr->siblings); if (container_ptr->leaf) { free_params(&container_ptr->children); } else { free_containers(&container_ptr->children); } free(container_ptr->name); free(container_ptr); } } static struct jack_parameter_container * find_container(struct jack_parameter_container * parent_ptr, const char * const * address, int max_depth) { struct list_head * node_ptr; struct jack_parameter_container * container_ptr; if (max_depth == 0 || *address == NULL) { return parent_ptr; } if (parent_ptr->leaf) { return NULL; } if (max_depth > 0) { max_depth--; } list_for_each(node_ptr, &parent_ptr->children) { container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); if (strcmp(container_ptr->name, *address) == 0) { if (container_ptr->symlink != NULL) { container_ptr = container_ptr->symlink; } return find_container(container_ptr, address + 1, max_depth); } } return NULL; } static bool init_leaf(struct list_head * parent_list_ptr, const char * name, const JSList * params_list, void * obj) { struct jack_parameter_container * container_ptr; container_ptr = create_container(parent_list_ptr, name); if (container_ptr == NULL) { return false; } container_ptr->leaf = true; container_ptr->obj = obj; while (params_list) { if (!add_controlapi_param(&container_ptr->children, params_list->data)) { return false; } params_list = jack_slist_next(params_list); } return true; } static bool init_engine(struct jack_params * params_ptr) { return init_leaf(¶ms_ptr->root.children, PTNODE_ENGINE, jackctl_server_get_parameters(params_ptr->server), NULL); } static bool init_drivers(struct jack_params * params_ptr) { const JSList * list; struct jack_parameter_container * container_ptr; container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVERS); if (container_ptr == NULL) { return false; } params_ptr->drivers_ptr = &container_ptr->children; params_ptr->drivers_count = 0; list = jackctl_server_get_drivers_list(params_ptr->server); while (list) { if (!init_leaf(&container_ptr->children, jackctl_driver_get_name(list->data), jackctl_driver_get_parameters(list->data), list->data)) { return false; } params_ptr->drivers_count++; list = jack_slist_next(list); } return true; } static bool init_internals(struct jack_params * params_ptr) { const JSList * list; struct jack_parameter_container * container_ptr; container_ptr = create_container(¶ms_ptr->root.children, PTNODE_INTERNALS); if (container_ptr == NULL) { return false; } list = jackctl_server_get_internals_list(params_ptr->server); while (list) { if (!init_leaf(&container_ptr->children, jackctl_internal_get_name(list->data), jackctl_internal_get_parameters(list->data), NULL)) { return false; } list = jack_slist_next(list); } return true; } static bool init_driver(struct jack_params * params_ptr) { struct jack_parameter_container * container_ptr; container_ptr = create_container(¶ms_ptr->root.children, PTNODE_DRIVER); if (container_ptr == NULL) { return false; } params_ptr->driver_ptr = container_ptr; return true; } #define params_ptr ((struct jack_params *)obj) static bool engine_driver_parameter_is_set(void * obj) { return params_ptr->driver_set; } static bool engine_driver_parameter_reset(void * obj) { if (!jack_params_set_driver(obj, DEFAULT_DRIVER)) { return false; } params_ptr->driver_set = false; return true; } union jackctl_parameter_value engine_driver_parameter_get_value(void * obj) { union jackctl_parameter_value value; strcpy(value.str, params_ptr->driver_ptr->symlink->name); return value; } bool engine_driver_parameter_set_value(void * obj, const union jackctl_parameter_value * value_ptr) { return jack_params_set_driver(obj, value_ptr->str); } union jackctl_parameter_value engine_driver_parameter_get_default_value(void * obj) { union jackctl_parameter_value value; strcpy(value.str, DEFAULT_DRIVER); return value; } #undef params_ptr static bool add_engine_driver_enum_constraint(void * context, const char * name) { strcpy((*((struct jack_parameter_enum **)context))->value.str, name); (*((struct jack_parameter_enum **)context))->short_desc = name; (*((struct jack_parameter_enum **)context))++; return true; } static bool init_engine_driver_parameter(struct jack_params * params_ptr) { struct jack_parameter * param_ptr; const char * address[PARAM_ADDRESS_SIZE] = {PTNODE_ENGINE, NULL}; struct jack_parameter_container * engine_ptr; struct jack_parameter_enum * possible_value; engine_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (engine_ptr == NULL) { return false; } param_ptr = malloc(sizeof(struct jack_parameter)); if (param_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_parameter"); goto fail; } param_ptr->ext = false; param_ptr->obj = params_ptr; param_ptr->vtable.is_set = engine_driver_parameter_is_set; param_ptr->vtable.reset = engine_driver_parameter_reset; param_ptr->vtable.get_value = engine_driver_parameter_get_value; param_ptr->vtable.set_value = engine_driver_parameter_set_value; param_ptr->vtable.get_default_value = engine_driver_parameter_get_default_value; param_ptr->type = JackParamString; param_ptr->name = "driver"; param_ptr->short_decr = "Driver to use"; param_ptr->long_descr = ""; param_ptr->constraint_flags = JACK_CONSTRAINT_FLAG_VALID | JACK_CONSTRAINT_FLAG_STRICT | JACK_CONSTRAINT_FLAG_FAKE_VALUE; param_ptr->constraint_range = false; param_ptr->constraint.enumeration.count = params_ptr->drivers_count; param_ptr->constraint.enumeration.possible_values_array = malloc(sizeof(struct jack_parameter_enum) * params_ptr->drivers_count); if (param_ptr->constraint.enumeration.possible_values_array == NULL) { goto free; } address[0] = PTNODE_DRIVERS; possible_value = param_ptr->constraint.enumeration.possible_values_array; jack_params_iterate_container((jack_params_handle)params_ptr, address, add_engine_driver_enum_constraint, &possible_value); list_add(¶m_ptr->siblings, &engine_ptr->children); return true; free: free(param_ptr); fail: return false; } jack_params_handle jack_params_create(jackctl_server_t * server) { struct jack_params * params_ptr; params_ptr = malloc(sizeof(struct jack_params)); if (params_ptr == NULL) { jack_error("Ran out of memory trying to allocate struct jack_params"); return NULL; } params_ptr->server = server; INIT_LIST_HEAD(¶ms_ptr->root.children); params_ptr->root.leaf = false; params_ptr->root.name = NULL; if (!init_engine(params_ptr) || !init_drivers(params_ptr) || !init_driver(params_ptr) || !init_engine_driver_parameter(params_ptr) || !jack_params_set_driver((jack_params_handle)params_ptr, DEFAULT_DRIVER) || !init_internals(params_ptr)) { jack_params_destroy((jack_params_handle)params_ptr); return NULL; } params_ptr->driver_set = false; assert(strcmp(params_ptr->driver_ptr->symlink->name, DEFAULT_DRIVER) == 0); return (jack_params_handle)params_ptr; } #define params_ptr ((struct jack_params *)params) void jack_params_destroy(jack_params_handle params) { free_containers(¶ms_ptr->root.children); free(params); } bool jack_params_set_driver(jack_params_handle params, const char * name) { struct list_head * node_ptr; struct jack_parameter_container * container_ptr; list_for_each(node_ptr, params_ptr->drivers_ptr) { container_ptr = list_entry(node_ptr, struct jack_parameter_container, siblings); if (strcmp(container_ptr->name, name) == 0) { params_ptr->driver_ptr->symlink = container_ptr; params_ptr->driver_set = true; return true; } } return false; } jackctl_driver_t * jack_params_get_driver(jack_params_handle params) { return params_ptr->driver_ptr->symlink->obj; } bool jack_params_check_address(jack_params_handle params, const char * const * address, bool want_leaf) { struct jack_parameter_container * container_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL) { return false; } if (want_leaf && !container_ptr->leaf) { return false; } return true; } bool jack_params_is_leaf_container(jack_params_handle params, const char * const * address) { struct jack_parameter_container * container_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL) { assert(false); return false; } return container_ptr->leaf; } bool jack_params_iterate_container( jack_params_handle params, const char * const * address, bool (* callback)(void * context, const char * name), void * context) { struct jack_parameter_container * container_ptr; struct list_head * node_ptr; const char * name; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL) { assert(false); return true; } list_for_each(node_ptr, &container_ptr->children) { if (container_ptr->leaf) { name = list_entry(node_ptr, struct jack_parameter, siblings)->name; } else { name = list_entry(node_ptr, struct jack_parameter_container, siblings)->name; } if (!callback(context, name)) { return false; } } return true; } bool jack_params_iterate_params( jack_params_handle params, const char * const * address, bool (* callback)(void * context, const struct jack_parameter * param_ptr), void * context) { struct jack_parameter_container * container_ptr; struct list_head * node_ptr; struct jack_parameter * param_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL || !container_ptr->leaf) { assert(false); return true; } list_for_each(node_ptr, &container_ptr->children) { param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); if (!callback(context, param_ptr)) { return false; } } return true; } const struct jack_parameter * jack_params_get_parameter(jack_params_handle params, const char * const * address) { int depth; struct jack_parameter_container * container_ptr; struct list_head * node_ptr; struct jack_parameter * param_ptr; for (depth = 0; depth < PARAM_ADDRESS_SIZE; depth++) { if (address[depth] == NULL) { break; } } depth--; container_ptr = find_container(¶ms_ptr->root, address, depth); if (container_ptr == NULL || !container_ptr->leaf) { return NULL; } list_for_each(node_ptr, &container_ptr->children) { param_ptr = list_entry(node_ptr, struct jack_parameter, siblings); if (strcmp(param_ptr->name, address[depth]) == 0) { return param_ptr; } } return NULL; } void jack_params_add_parameter(jack_params_handle params, const char * const * address, bool end, struct jack_parameter * param_ptr) { struct jack_parameter_container * container_ptr; container_ptr = find_container(¶ms_ptr->root, address, PARAM_ADDRESS_SIZE); if (container_ptr == NULL || !container_ptr->leaf) { assert(false); return; } param_ptr->ext = true; if (end) { list_add_tail(¶m_ptr->siblings, &container_ptr->children); } else { list_add(¶m_ptr->siblings, &container_ptr->children); } return; } #undef params_ptr