diff --git a/src/tools/pw-link.c b/src/tools/pw-link.c index a7cd65d97..08a3301d7 100644 --- a/src/tools/pw-link.c +++ b/src/tools/pw-link.c @@ -29,6 +29,13 @@ struct object { uint32_t extra[2]; }; +struct target_link { + struct spa_list link; + struct pw_proxy *proxy; + struct spa_hook listener; + int result; +}; + struct data { struct pw_main_loop *loop; @@ -56,6 +63,7 @@ struct data { struct spa_hook registry_listener; struct spa_list objects; + struct spa_list target_links; int sync; int link_res; @@ -70,8 +78,8 @@ struct data { static void link_proxy_error(void *data, int seq, int res, const char *message) { - struct data *d = data; - d->link_res = res; + struct target_link *tl = data; + tl->result = res; } static const struct pw_proxy_events link_proxy_events = { @@ -84,34 +92,6 @@ static void core_sync(struct data *data) data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); } -static int create_link(struct data *data) -{ - struct pw_proxy *proxy; - struct spa_hook listener; - - data->link_res = 0; - - proxy = pw_core_create_object(data->core, - "link-factory", - PW_TYPE_INTERFACE_Link, - PW_VERSION_LINK, - &data->props->dict, 0); - if (proxy == NULL) - return -errno; - - spa_zero(listener); - pw_proxy_add_listener(proxy, &listener, &link_proxy_events, data); - - core_sync(data); - pw_main_loop_run(data->loop); - - spa_hook_remove(&listener); - - pw_proxy_destroy(proxy); - - return data->link_res; -} - static struct object *find_object(struct data *data, uint32_t type, uint32_t id) { struct object *o; @@ -338,7 +318,34 @@ static void do_list(struct data *data) } } -static int do_link_ports(struct data *data) +static int create_link_target(struct data *data) +{ + struct target_link *tl = calloc(1, sizeof(*tl)); + if (!tl) + return -ENOMEM; + + tl->proxy = pw_core_create_object(data->core, + "link-factory", PW_TYPE_INTERFACE_Link, + PW_VERSION_LINK, &data->props->dict, 0); + if (tl->proxy == NULL) + return -errno; + + pw_proxy_add_listener(tl->proxy, &tl->listener, &link_proxy_events, tl); + spa_list_append(&data->target_links, &tl->link); + return 0; +} + +/* + * create_link_proxies() looks at the current objects and tries to find the + * matching output and input nodes (multiple links) or the matching output and + * input ports. + * + * If successful, it fills data->target_links with proxies for all links and + * returns the number of links. This can be zero (two nodes with no ports). It + * might return (negative) errors. -ENOENT means no matching nodes or ports + * were found. + */ +static int create_link_proxies(struct data *data) { uint32_t in_port = 0, out_port = 0; struct object *n, *p; @@ -372,9 +379,8 @@ static int do_link_ports(struct data *data) } if (in_node && out_node) { - int i, ret; + int i; char port_id[32]; - bool all_links_exist = true; for (i=0;; i++) { snprintf(port_id, sizeof(port_id), "%d", i); @@ -382,27 +388,17 @@ static int do_link_ports(struct data *data) struct object *port_out = find_node_port(data, out_node, PW_DIRECTION_OUTPUT, port_id); struct object *port_in = find_node_port(data, in_node, PW_DIRECTION_INPUT, port_id); - if (!port_out && !port_in) { - fprintf(stderr, "Input & output port do not exist\n"); - goto no_port; - } else if (!port_in) { - fprintf(stderr, "Input port does not exist\n"); - goto no_port; - } else if (!port_out) { - fprintf(stderr, "Output port does not exist\n"); - goto no_port; - } + if (!port_out || !port_in) + return i; pw_properties_setf(data->props, PW_KEY_LINK_OUTPUT_PORT, "%u", port_out->id); pw_properties_setf(data->props, PW_KEY_LINK_INPUT_PORT, "%u", port_in->id); - if ((ret = create_link(data)) < 0 && ret != -EEXIST) + int ret = create_link_target(data); + if (ret) return ret; - - if (ret >= 0) - all_links_exist = false; } - return (all_links_exist ? -EEXIST : 0); + return i; } if (in_port == 0 || out_port == 0) @@ -411,10 +407,12 @@ static int do_link_ports(struct data *data) pw_properties_setf(data->props, PW_KEY_LINK_OUTPUT_PORT, "%u", out_port); pw_properties_setf(data->props, PW_KEY_LINK_INPUT_PORT, "%u", in_port); - return create_link(data); - -no_port: - return -ENOENT; + /* + * create_link_target() returns zero on success. We return the number of + * links on success, meaning 1. + */ + int ret = create_link_target(data); + return !ret ? 1 : ret; } static int do_unlink_ports(struct data *data) @@ -718,6 +716,7 @@ int main(int argc, char *argv[]) setlocale(LC_ALL, ""); pw_init(&argc, &argv); spa_list_init(&data.objects); + spa_list_init(&data.target_links); setlinebuf(stdout); @@ -867,10 +866,26 @@ int main(int argc, char *argv[]) return -1; } } else { - if ((res = do_link_ports(&data)) < 0) { - fprintf(stderr, "failed to link ports: %s\n", spa_strerror(res)); + int nb_links = create_link_proxies(&data); + if (nb_links < 0) { + fprintf(stderr, "failed to link ports: %s\n", spa_strerror(nb_links)); return -1; } + + if (nb_links > 0) { + core_sync(&data); + pw_main_loop_run(data.loop); + + struct target_link *tl; + spa_list_for_each(tl, &data.target_links, link) { + if (tl->result) { + fprintf(stderr, "failed to link ports: %s\n", + spa_strerror(tl->result)); + return -1; + } + } + } + } if (data.opt_mode & MODE_MONITOR) { @@ -879,6 +894,12 @@ int main(int argc, char *argv[]) data.monitoring = false; } + struct target_link *tl; + spa_list_for_each(tl, &data.target_links, link) { + spa_hook_remove(&tl->listener); + pw_proxy_destroy(tl->proxy); + } + if (data.out_regex) regfree(data.out_regex); if (data.in_regex)