diff --git a/man/pw-top.1.rst.in b/man/pw-top.1.rst.in index 0e3a4854e..ab8569b1f 100644 --- a/man/pw-top.1.rst.in +++ b/man/pw-top.1.rst.in @@ -27,13 +27,15 @@ a tree-like representation. The columns presented are as follows: S - Measurement status. - ! representing inactive - no connections - - Blank representing active + Node status. + E = ERROR + C = CREATING + S = SUSPENDED + I = IDLE + R = RUNNING ID - The ID of the pipewire node/device, as found in *pw-dump* + The ID of the pipewire node/device, as found in *pw-dump* and *pw-cli* QUANT The current quantum (for drivers) and the suggested quantum for follower @@ -135,6 +137,8 @@ FORMAT For raw audio formats, the layout is . + For IEC958 passthrough audio formats, the layout is IEC958 . + For DSD formats, the layout is . For Video formats, the layout is x. diff --git a/src/tools/pw-top.c b/src/tools/pw-top.c index 92f73152c..dfaa32af6 100644 --- a/src/tools/pw-top.c +++ b/src/tools/pw-top.c @@ -62,8 +62,10 @@ struct measurement { struct node { struct spa_list link; + struct data *data; uint32_t id; char name[MAX_NAME+1]; + enum pw_node_state state; struct measurement measurement; struct driver info; struct node *driver; @@ -73,6 +75,7 @@ struct node { char format[MAX_FORMAT+1]; struct pw_proxy *proxy; struct spa_hook proxy_listener; + unsigned int inactive:1; struct spa_hook object_listener; }; @@ -95,7 +98,7 @@ struct data { int n_nodes; struct spa_list node_list; uint32_t generation; - unsigned have_data:1; + unsigned pending_refresh:1; WINDOW *win; }; @@ -160,12 +163,29 @@ static const struct pw_proxy_events proxy_events = { .destroy = on_node_destroy, }; +static void do_refresh(struct data *d); + +static void node_info(void *data, const struct pw_node_info *info) +{ + struct node *n = data; + + if (n->state != info->state) { + n->state = info->state; + do_refresh(n->data); + } +} + static void node_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { struct node *n = data; + if (param == NULL) { + spa_zero(n->format); + goto done; + } + switch (id) { case SPA_PARAM_Format: { @@ -238,10 +258,13 @@ static void node_param(void *data, int seq, default: break; } +done: + do_refresh(n->data); } static const struct pw_node_events node_events = { PW_VERSION_NODE, + .info = node_info, .param = node_param, }; @@ -256,6 +279,7 @@ static struct node *add_node(struct data *d, uint32_t id, const char *name) strncpy(n->name, name, MAX_NAME); else snprintf(n->name, sizeof(n->name), "%u", id); + n->data = d; n->id = id; n->driver = n; n->proxy = pw_registry_bind(d->registry, id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0); @@ -272,6 +296,7 @@ static struct node *add_node(struct data *d, uint32_t id, const char *name) } spa_list_append(&d->node_list, &n->link); d->n_nodes++; + d->pending_refresh = true; return n; } @@ -282,6 +307,7 @@ static void remove_node(struct data *d, struct node *n) pw_proxy_destroy(n->proxy); spa_list_remove(&n->link); d->n_nodes--; + d->pending_refresh = true; free(n); } @@ -346,7 +372,10 @@ static int process_follower_block(struct data *d, const struct spa_pod *pod, str return -ENOENT; n->measurement = m; - n->driver = point->driver; + if (n->driver != point->driver) { + n->driver = point->driver; + d->pending_refresh = true; + } n->generation = d->generation; if (m.status != 3) { n->errors++; @@ -356,9 +385,9 @@ static int process_follower_block(struct data *d, const struct spa_pod *pod, str return 0; } -static const char *print_time(char *buf, size_t len, uint64_t val) +static const char *print_time(char *buf, bool active, size_t len, uint64_t val) { - if (val == (uint64_t)-1) + if (val == (uint64_t)-1 || !active) snprintf(buf, len, " --- "); else if (val == (uint64_t)-2) snprintf(buf, len, " +++ "); @@ -371,9 +400,9 @@ static const char *print_time(char *buf, size_t len, uint64_t val) return buf; } -static const char *print_perc(char *buf, size_t len, uint64_t val, float quantum) +static const char *print_perc(char *buf, bool active, size_t len, uint64_t val, float quantum) { - if (val == (uint64_t)-1) { + if (val == (uint64_t)-1 || !active) { snprintf(buf, len, " --- "); } else if (val == (uint64_t)-2) { snprintf(buf, len, " +++ "); @@ -384,6 +413,23 @@ static const char *print_perc(char *buf, size_t len, uint64_t val, float quantum return buf; } +static const char *state_as_string(enum pw_node_state state) +{ + switch (state) { + case PW_NODE_STATE_ERROR: + return "E"; + case PW_NODE_STATE_CREATING: + return "C"; + case PW_NODE_STATE_SUSPENDED: + return "S"; + case PW_NODE_STATE_IDLE: + return "I"; + case PW_NODE_STATE_RUNNING: + return "R"; + } + return "!"; +} + static void print_node(struct data *d, struct driver *i, struct node *n, int y) { char buf1[64]; @@ -393,8 +439,13 @@ static void print_node(struct data *d, struct driver *i, struct node *n, int y) uint64_t waiting, busy; float quantum; struct spa_fraction frac; + bool active; - if (n->driver == n) + active = n->state == PW_NODE_STATE_RUNNING || n->state == PW_NODE_STATE_IDLE; + + if (!active) + frac = SPA_FRACTION(0, 0); + else if (n->driver == n) frac = SPA_FRACTION((uint32_t)(i->clock.duration * i->clock.rate.num), i->clock.rate.denom); else frac = SPA_FRACTION(n->measurement.latency.num, n->measurement.latency.denom); @@ -419,19 +470,28 @@ static void print_node(struct data *d, struct driver *i, struct node *n, int y) busy = -1; mvwprintw(d->win, y, 0, "%s %4.1u %6.1u %6.1u %s %s %s %s %3.1u %16.16s %s%s", - n->measurement.status != 3 ? "!" : " ", + state_as_string(n->state), n->id, frac.num, frac.denom, - print_time(buf1, 64, waiting), - print_time(buf2, 64, busy), - print_perc(buf3, 64, waiting, quantum), - print_perc(buf4, 64, busy, quantum), + print_time(buf1, active, 64, waiting), + print_time(buf2, active, 64, busy), + print_perc(buf3, active, 64, waiting, quantum), + print_perc(buf4, active, 64, busy, quantum), i->xrun_count + n->errors, - n->measurement.status != 3 ? "" : n->format, + active ? n->format : "", n->driver == n ? "" : " + ", n->name); } +static void clear_node(struct node *n) +{ + n->driver = n; + spa_zero(n->measurement); + spa_zero(n->info); + n->errors = 0; + n->last_error_status = 0; +} + static void do_refresh(struct data *d) { struct node *n, *t, *f; @@ -452,14 +512,8 @@ static void do_refresh(struct data *d) break; spa_list_for_each(f, &d->node_list, link) { - if (d->generation > f->generation + 2) { - f->driver = f; - spa_zero(f->measurement); - spa_zero(f->info); - spa_zero(f->format); - f->errors = 0; - f->last_error_status = 0; - } + if (d->generation > f->generation + 22) + clear_node(f); if (f->driver != n || f == n) continue; @@ -476,6 +530,7 @@ static void do_refresh(struct data *d) wclrtobot(d->win); wrefresh(d->win); + d->pending_refresh = false; } static void do_timeout(void *data, uint64_t expirations) @@ -521,10 +576,8 @@ static void profiler_profile(void *data, const struct spa_pod *pod) if (res < 0) continue; } - if (!d->have_data) { - d->have_data = true; + if (d->pending_refresh) do_refresh(d); - } } static const struct pw_profiler_events profiler_events = { @@ -563,7 +616,8 @@ static void registry_event_global(void *data, uint32_t id, d->profiler = proxy; pw_proxy_add_object_listener(proxy, &d->profiler_listener, &profiler_events, d); } - + if (d->pending_refresh) + do_refresh(d); return; error_proxy: @@ -577,6 +631,8 @@ static void registry_event_global_remove(void *data, uint32_t id) struct node *n; if ((n = find_node(d, id)) != NULL) remove_node(d, n); + if (d->pending_refresh) + do_refresh(d); } static const struct pw_registry_events registry_events = {