--- PROTOCOL | 31 +++++++++++ configure.ac | 2 +- src/map-file | 3 ++ src/pulse/introspect.c | 111 ++++++++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 41 +++++++++++++++ src/pulsecore/native-common.h | 4 ++ src/pulsecore/protocol-native.c | 46 +++++++++++++---- 7 files changed, 226 insertions(+), 12 deletions(-) diff --git a/PROTOCOL b/PROTOCOL index 185b91a..e0c0a64 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -342,6 +342,37 @@ SUBCOMMAND_SAVE_FORMATS, in reply from SUBCOMMAND_READ_FORMATS[_ALL] (uint8_t ) PA_ENCODING_MPEG2_AAC_IEC61937 := 6 +## v29, implemented by >= 5.0 + +New client->server commands: + GET_NODE_INFO + GET_NODE_INFO_LIST + +GET_NODE_INFO parameters: + uint32_t index + string name + +When getting the node info by index, the index parameter must not be +PA_INVALID_INDEX and the name parameter must be NULL. + +When getting the node info by name, the index parameter must be +PA_INVALID_INDEX and the name parameter must not be NULL or an empty string. + +GET_NODE_INFO_LIST doesn't have parameters. + +GET_NODE_INFO reply parameters: + uint32_t index + string name + string description + pa_direction_t direction + +The index must not be PA_INVALID_INDEX. The name must not be NULL or empty. The +description must not be NULL or empty. The direction must be either OUTPUT or +INPUT, not both. + +GET_NODE_INFO_LIST reply parameters are otherwise the same as with +GET_NODE_INFO, but the parameter list is repeated for each node. + #### If you just changed the protocol, read this ## module-tunnel depends on the sink/source/sink-input/source-input protocol ## internals, so if you changed these, you might have broken module-tunnel. diff --git a/configure.ac b/configure.ac index 9cbcb6d..5a2bbab 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,7 @@ AC_SUBST(PA_MINOR, pa_minor) AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor) AC_SUBST(PA_API_VERSION, 12) -AC_SUBST(PA_PROTOCOL_VERSION, 28) +AC_SUBST(PA_PROTOCOL_VERSION, 29) # The stable ABI for client applications, for the version info x:y:z # always will hold y=z diff --git a/src/map-file b/src/map-file index 90c8ea2..562c48c 100644 --- a/src/map-file +++ b/src/map-file @@ -42,6 +42,9 @@ pa_context_get_client_info_list; pa_context_get_index; pa_context_get_module_info; pa_context_get_module_info_list; +pa_context_get_node_info_by_index; +pa_context_get_node_info_by_name; +pa_context_get_node_info_list; pa_context_get_protocol_version; pa_context_get_sample_info_by_index; pa_context_get_sample_info_by_name; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 704e96e..53c5011 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -1777,6 +1777,117 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_cb_t) cb, userdata); } +/*** Routing node info ***/ + +static void context_get_node_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + pa_assert(pd); + pa_assert(o); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t, false) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_node_info info; + + pa_zero(info); + + if (pa_tagstruct_getu32(t, &info.index) < 0 || + info.index == PA_INVALID_INDEX || + pa_tagstruct_gets(t, &info.name) < 0 || + !info.name || !*info.name || + pa_tagstruct_gets(t, &info.description) < 0 || + !info.description || !*info.description || + pa_tagstruct_get_direction(t, &info.direction) || + (info.direction != PA_DIRECTION_OUTPUT && info.direction != PA_DIRECTION_INPUT)) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_node_info_cb_t cb = (pa_node_info_cb_t) o->callback; + cb(o->context, &info, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_node_info_cb_t cb = (pa_node_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation *pa_context_get_node_info_by_name(pa_context *c, const char *name, pa_node_info_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(name); + pa_assert(*name); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 29, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_NODE_INFO, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_node_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation *pa_context_get_node_info_by_index(pa_context *c, uint32_t idx, pa_node_info_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(idx != PA_INVALID_INDEX); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 29, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_NODE_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_node_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation *pa_context_get_node_info_list(pa_context *c, pa_node_info_cb_t cb, void *userdata) { + pa_assert(c); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 29, PA_ERR_NOTSUPPORTED); + + return pa_context_send_simple_command(c, PA_COMMAND_GET_NODE_INFO_LIST, context_get_node_info_callback, (pa_operation_cb_t) cb, userdata); +} + static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_context_success_cb_t cb, void *userdata) { pa_operation *o; pa_tagstruct *t; diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index a833471..dadde48 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -665,6 +665,47 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t /** @} */ +/** @{ \name Routing Nodes */ + +/** Routing node information. Please note that this structure can be extended + * as part of evolutionary API updates at any time in any new release. + * + * \since 5.0 */ +typedef struct pa_node_info { + /** The index of this node. */ + uint32_t index; + + /** The name of this node. While one name can refer to only one node at any + * given time, the same name can still refer to multiple nodes over time, + * and the same node may have a different name e.g. after server restart. + * This means that nodes don't have any identifier that could be e.g. saved + * to disk for referring to the same node later. There may be some nodes in + * the future that have a fixed name, but currently there are none. */ + const char *name; + + /** The human-readable description of this node. This is localized and + * suitable to be shown in user interfaces as the label for this node. */ + const char *description; + + /** The direction of this node. */ + pa_direction_t direction; +} pa_node_info; + +/** Callback prototype for pa_context_get_node_info_by_name() and friends. + * \since 5.0 */ +typedef void (*pa_node_info_cb_t)(pa_context *c, const pa_node_info *info, int eol, void *userdata); + +/** Get information about a node by its name. \since 5.0 */ +pa_operation *pa_context_get_node_info_by_name(pa_context *c, const char *name, pa_node_info_cb_t cb, void *userdata); + +/** Get information about a node by its index. \since 5.0 */ +pa_operation *pa_context_get_node_info_by_index(pa_context *c, uint32_t idx, pa_node_info_cb_t cb, void *userdata); + +/** Get a list of all nodes. \since 5.0 */ +pa_operation *pa_context_get_node_info_list(pa_context *c, pa_node_info_cb_t cb, void *userdata); + +/** @} */ + /** \cond fulldocs */ /** @{ \name Autoload Entries */ diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index dad82e0..7898118 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -176,6 +176,10 @@ enum { /* Supported since protocol v27 (3.0) */ PA_COMMAND_SET_PORT_LATENCY_OFFSET, + /* Supported since protocol v29 (5.0) */ + PA_COMMAND_GET_NODE_INFO, + PA_COMMAND_GET_NODE_INFO_LIST, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index a73787d..94b6e9f 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -325,6 +325,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info, [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info, [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info, + [PA_COMMAND_GET_NODE_INFO] = command_get_info, [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list, @@ -333,6 +334,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_NODE_INFO_LIST] = command_get_info_list, [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info, [PA_COMMAND_SUBSCRIBE] = command_subscribe, @@ -3452,6 +3454,16 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s pa_tagstruct_put_proplist(t, e->proplist); } +static void node_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_node *node) { + pa_assert(t); + pa_assert(node); + + pa_tagstruct_putu32(t, node->index); + pa_tagstruct_puts(t, node->name); + pa_tagstruct_puts(t, node->description); + pa_tagstruct_put_direction(t, node->direction); +} + static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); uint32_t idx; @@ -3463,6 +3475,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p pa_sink_input *si = NULL; pa_source_output *so = NULL; pa_scache_entry *sce = NULL; + pa_node *node = NULL; const char *name = NULL; pa_tagstruct *reply; @@ -3516,15 +3529,20 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO) so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); - else { - pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO); + else if (command == PA_COMMAND_GET_SAMPLE_INFO) { if (idx != PA_INVALID_INDEX) sce = pa_idxset_get_by_index(c->protocol->core->scache, idx); else sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE); - } + } else if (command == PA_COMMAND_GET_NODE_INFO) { + if (idx != PA_INVALID_INDEX) + node = pa_idxset_get_by_index(c->protocol->core->nodes, idx); + else + node = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_NODE); + } else + pa_assert_not_reached(); - if (!sink && !source && !client && !card && !module && !si && !so && !sce) { + if (!sink && !source && !client && !card && !module && !si && !so && !sce && !node) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); return; } @@ -3544,8 +3562,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p sink_input_fill_tagstruct(c, reply, si); else if (so) source_output_fill_tagstruct(c, reply, so); - else + else if (sce) scache_fill_tagstruct(c, reply, sce); + else if (node) + node_fill_tagstruct(c, reply, node); pa_pstream_send_tagstruct(c->pstream, reply); } @@ -3582,10 +3602,12 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t i = c->protocol->core->sink_inputs; else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) i = c->protocol->core->source_outputs; - else { - pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); + else if (command == PA_COMMAND_GET_SAMPLE_INFO_LIST) i = c->protocol->core->scache; - } + else if (command == PA_COMMAND_GET_NODE_INFO_LIST) + i = c->protocol->core->nodes; + else + pa_assert_not_reached(); if (i) { PA_IDXSET_FOREACH(p, i, idx) { @@ -3603,10 +3625,12 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t sink_input_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) source_output_fill_tagstruct(c, reply, p); - else { - pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); + else if (command == PA_COMMAND_GET_SAMPLE_INFO_LIST) scache_fill_tagstruct(c, reply, p); - } + else if (command == PA_COMMAND_GET_NODE_INFO_LIST) + node_fill_tagstruct(c, reply, p); + else + pa_assert_not_reached(); } } -- 1.8.1.2