From 6a7d83f051810b5d382eefc41070292d6ef9539b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Lebrun?= Date: Tue, 30 Jan 2024 22:14:27 +0100 Subject: [PATCH] pw-link: listen to link info events to detect link creation Stop using sync for link creation detection. Instead, listen to link events. We still listen to errors, link info events are not enough. For example, if a link already exists, we only get a proxy error and no link info event. --- src/tools/pw-link.c | 56 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/tools/pw-link.c b/src/tools/pw-link.c index db33a8bba..6fd032a69 100644 --- a/src/tools/pw-link.c +++ b/src/tools/pw-link.c @@ -31,8 +31,10 @@ struct object { struct target_link { struct spa_list link; + struct data *data; struct pw_proxy *proxy; - struct spa_hook listener; + struct spa_hook listener, link_listener; + enum pw_link_state state; int result; }; @@ -78,10 +80,34 @@ struct data { regex_t in_port_regex, *in_regex; }; +static void link_event(struct target_link *tl, enum pw_link_state state, int result) +{ + /* Ignore non definitive states (negociating, allocating, etc). */ + if (state != PW_LINK_STATE_ERROR && + state != PW_LINK_STATE_PAUSED && + state != PW_LINK_STATE_ACTIVE) + return; + + /* Keep the first definitive state. For example, once a link is marked + * as paused, we start ignoring all errors. */ + if (tl->state == PW_LINK_STATE_INIT) { + tl->state = state; + tl->result = result; + } + + /* End if all links have a definitive state. */ + struct target_link *m; + spa_list_for_each(m, &tl->data->target_links, link) { + if (m->state == PW_LINK_STATE_INIT) + return; + } + pw_main_loop_quit(tl->data->loop); +} + static void link_proxy_error(void *data, int seq, int res, const char *message) { struct target_link *tl = data; - tl->result = res; + link_event(tl, PW_LINK_STATE_ERROR, res); } static const struct pw_proxy_events link_proxy_events = { @@ -89,6 +115,26 @@ static const struct pw_proxy_events link_proxy_events = { .error = link_proxy_error, }; +static void link_event_info(void *data, const struct pw_link_info *info) +{ + struct target_link *tl = data; + int result = 0; + + /* + * Invent an error code to always have one if state == error. That does + * not occur currently; on link error a proxy error is raised first. + */ + if (tl->state == PW_LINK_STATE_ERROR) + result = -EINVAL; + + link_event(tl, info->state, result); +} + +static const struct pw_link_events link_events = { + PW_VERSION_LINK_EVENTS, + .info = link_event_info, +}; + static void core_sync(struct data *data) { data->sync = pw_core_sync(data->core, PW_ID_CORE, data->sync); @@ -332,7 +378,10 @@ static int create_link_target(struct data *data) if (tl->proxy == NULL) return -errno; + tl->data = data; + tl->state = PW_LINK_STATE_INIT; pw_proxy_add_listener(tl->proxy, &tl->listener, &link_proxy_events, tl); + pw_proxy_add_object_listener(tl->proxy, &tl->link_listener, &link_events, tl); spa_list_append(&data->target_links, &tl->link); return 0; } @@ -898,12 +947,11 @@ int main(int argc, char *argv[]) } if (data.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) { + if (tl->state == PW_LINK_STATE_ERROR) { fprintf(stderr, "failed to link ports: %s\n", spa_strerror(tl->result)); return -1;