/* -*- Mode: C ; c-basic-offset: 2 -*- */ /***************************************************************************** * * DESCRIPTION: * * * NOTES: * * *****************************************************************************/ #include "load.h" #include "limits.h" #include "studio.h" #include "../proxies/jmcore_proxy.h" void ladish_dump_element_stack(struct ladish_parse_context * context_ptr) { signed int depth; const char * descr; log_info("depth=%d", context_ptr->depth); for (depth = context_ptr->depth; depth >= 0; depth--) { switch (context_ptr->element[depth]) { case PARSE_CONTEXT_ROOT: descr = "root"; break; case PARSE_CONTEXT_STUDIO: descr = "studio"; break; case PARSE_CONTEXT_JACK: descr = "jack"; break; case PARSE_CONTEXT_CONF: descr = "conf"; break; case PARSE_CONTEXT_PARAMETER: descr = "parameter"; break; case PARSE_CONTEXT_CLIENTS: descr = "clients"; break; case PARSE_CONTEXT_CLIENT: descr = "client"; break; case PARSE_CONTEXT_PORTS: descr = "ports"; break; case PARSE_CONTEXT_PORT: descr = "port"; break; case PARSE_CONTEXT_DICT: descr = "dict"; break; case PARSE_CONTEXT_KEY: descr = "key"; break; case PARSE_CONTEXT_CONNECTIONS: descr = "connections"; break; case PARSE_CONTEXT_CONNECTION: descr = "connection"; break; case PARSE_CONTEXT_APPLICATIONS: descr = "applications"; break; case PARSE_CONTEXT_APPLICATION: descr = "application"; break; case PARSE_CONTEXT_ROOMS: descr = "rooms"; break; case PARSE_CONTEXT_ROOM: descr = "room"; break; case PARSE_CONTEXT_PROJECT: descr = "project"; break; default: descr = "?"; } log_info("%d - %u (%s)", depth, context_ptr->element[depth], descr); } } static const char * get_string_attribute_internal(const char * const * attr, const char * key, bool optional) { while (attr[0] != NULL) { ASSERT(attr[1] != NULL); if (strcmp(attr[0], key) == 0) { return attr[1]; } attr += 2; } if (!optional) { log_error("attribute \"%s\" is missing", key); } return NULL; } const char * ladish_get_string_attribute(const char * const * attr, const char * key) { return get_string_attribute_internal(attr, key, false); } const char * ladish_get_uuid_attribute(const char * const * attr, const char * key, uuid_t uuid, bool optional) { const char * value; value = get_string_attribute_internal(attr, key, optional); if (value == NULL) { return NULL; } if (uuid_parse(value, uuid) != 0) { log_error("cannot parse uuid \"%s\"", value); return NULL; } return value; } const char * ladish_get_bool_attribute(const char * const * attr, const char * key, bool * bool_value_ptr) { const char * value_str; value_str = ladish_get_string_attribute(attr, key); if (value_str == NULL) { return NULL; } if (strcmp(value_str, "true") == 0) { *bool_value_ptr = true; return value_str; } if (strcmp(value_str, "false") == 0) { *bool_value_ptr = false; return value_str; } log_error("boolean XML attribute has value of \"%s\" but only \"true\" and \"false\" are valid", value_str); return NULL; } const char * ladish_get_byte_attribute(const char * const * attr, const char * key, uint8_t * byte_value_ptr) { const char * value_str; long int li_value; char * end_ptr; value_str = ladish_get_string_attribute(attr, key); if (value_str == NULL) { return NULL; } errno = 0; /* To distinguish success/failure after call */ li_value = strtol(value_str, &end_ptr, 10); if ((errno == ERANGE && (li_value == LONG_MAX || li_value == LONG_MIN)) || (errno != 0 && li_value == 0) || end_ptr == value_str) { log_error("value '%s' of attribute '%s' is not valid integer.", value_str, key); return NULL; } if (li_value < 0 || li_value > 255) { log_error("value '%s' of attribute '%s' is not valid uint8.", value_str, key); return NULL; } *byte_value_ptr = (uint8_t)li_value; return value_str; } bool ladish_get_name_and_uuid_attributes( const char * element_description, const char * const * attr, const char ** name_str_ptr, const char ** uuid_str_ptr, uuid_t uuid) { const char * name_str; const char * uuid_str; name_str = ladish_get_string_attribute(attr, "name"); if (name_str == NULL) { log_error("%s \"name\" attribute is not available", element_description); return false; } uuid_str = ladish_get_uuid_attribute(attr, "uuid", uuid, false); if (uuid_str == NULL) { log_error("%s \"uuid\" attribute is not available. name=\"%s\"", element_description, name_str); return false; } *name_str_ptr = name_str; *uuid_str_ptr = uuid_str; return true; } bool ladish_parse_port_type_and_direction_attributes( const char * element_description, const char * const * attr, uint32_t * type_ptr, uint32_t * flags_ptr) { const char * type_str; const char * direction_str; type_str = ladish_get_string_attribute(attr, "type"); if (type_str == NULL) { log_error("%s \"type\" attribute is not available", element_description); return false; } direction_str = ladish_get_string_attribute(attr, "direction"); if (direction_str == NULL) { log_error("%s \"direction\" attribute is not available", element_description); return false; } if (strcmp(type_str, "midi") == 0) { *type_ptr = JACKDBUS_PORT_TYPE_MIDI; } else if (strcmp(type_str, "audio") == 0) { *type_ptr = JACKDBUS_PORT_TYPE_AUDIO; } else { log_error("%s \"type\" attribute contains unknown value \"%s\"", element_description, type_str); return false; } if (strcmp(direction_str, "playback") == 0) { *flags_ptr = 0; JACKDBUS_PORT_SET_INPUT(*flags_ptr); } else if (strcmp(direction_str, "capture") == 0) { *flags_ptr = 0; JACKDBUS_PORT_SET_OUTPUT(*flags_ptr); } else { log_error("%s \"direction\" attribute contains unknown value \"%s\"", element_description, direction_str); return false; } return true; } struct interlink_context { ladish_graph_handle vgraph; ladish_app_supervisor_handle app_supervisor; }; #define ctx_ptr ((struct interlink_context *)context) static bool interlink_client( void * context, ladish_graph_handle UNUSED(graph_handle), bool UNUSED(hidden), ladish_client_handle jclient, const char * name, void ** UNUSED(client_iteration_context_ptr_ptr)) { uuid_t app_uuid; uuid_t vclient_app_uuid; uuid_t vclient_uuid; ladish_client_handle vclient; pid_t pid; ladish_app_handle app; bool interlinked; bool jmcore; ladish_graph_handle vgraph; ASSERT(ctx_ptr->vgraph != NULL); vgraph = ladish_client_get_vgraph(jclient); if (vgraph != NULL && ctx_ptr->vgraph != vgraph) { /* skip clients of different vgraphs */ return true; } if (strcmp(name, "system") == 0) { return true; } interlinked = ladish_client_get_interlink(jclient, vclient_uuid); if (ladish_client_has_app(jclient)) { ASSERT(interlinked); /* jclient has app associated but is not interlinked */ return true; } else if (interlinked) { /* jclient has no app associated but is interlinked */ /* this can happen if there is an external app (presumably in a different vgraph) */ ASSERT_NO_PASS; /* if vgraph is different, then we should have skipped the client earlier */ return true; } ASSERT(!interlinked); /* XXX: why this is is here is a mystery. interlink is supposed to be called just after load so pid will always be 0 */ pid = ladish_client_get_pid(jclient); jmcore = pid != 0 && pid == jmcore_proxy_get_pid_cached(); if (jmcore) { return true; } if (ladish_virtualizer_is_a2j_client(jclient)) { return true; } app = ladish_app_supervisor_find_app_by_name(ctx_ptr->app_supervisor, name); if (app == NULL) { log_info("JACK client \"%s\" not found in app supervisor", name); return true; } vclient = ladish_graph_find_client_by_name(ctx_ptr->vgraph, name, false); if (vclient == NULL) { log_error("JACK client '%s' has no vclient associated", name); return true; } log_info("Interlinking clients of app '%s'", name); ladish_client_interlink(jclient, vclient); ladish_app_get_uuid(app, app_uuid); if (ladish_client_get_app(vclient, vclient_app_uuid)) { if (uuid_compare(app_uuid, vclient_app_uuid) != 0) { log_error("vclient of app '%s' already has a different app uuid", name); } } else { log_info("associating vclient with app '%s'", name); ladish_client_set_app(vclient, app_uuid); } ladish_client_set_app(jclient, app_uuid); ladish_client_set_vgraph(jclient, ctx_ptr->vgraph); return true; } bool interlink_port( void * UNUSED(context), ladish_graph_handle UNUSED(graph_handle), bool UNUSED(hidden), void * UNUSED(client_iteration_context_ptr), ladish_client_handle client_handle, const char * UNUSED(client_name), ladish_port_handle port_handle, const char * UNUSED(port_name), uint32_t UNUSED(port_type), uint32_t UNUSED(port_flags)) { uuid_t app_uuid; if (ladish_client_get_app(client_handle, app_uuid)) { ladish_port_set_app(port_handle, app_uuid); } return true; } void ladish_interlink(ladish_graph_handle vgraph, ladish_app_supervisor_handle app_supervisor) { struct interlink_context ctx; ctx.vgraph = vgraph; ctx.app_supervisor = app_supervisor; ladish_graph_iterate_nodes(ladish_studio_get_jack_graph(), &ctx, interlink_client, NULL, NULL); ladish_graph_iterate_nodes(vgraph, &ctx, NULL, interlink_port, NULL); }