For better readability, "pactl list message-handlers" is introduced which prints a formatted output of "pactl send-message /core list-handlers". The patch also adds the function pa_split_message_parameter_string() for easy parsing of the message response string. --- man/pactl.1.xml.in | 2 +- shell-completion/bash/pulseaudio | 2 +- shell-completion/zsh/_pulseaudio | 1 + src/pulse/util.c | 79 ++++++++++++++++++++++++++++++++++++++++ src/pulse/util.h | 4 ++ src/utils/pactl.c | 60 +++++++++++++++++++++++++++++- 6 files changed, 144 insertions(+), 4 deletions(-) diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in index 4052fae3..66c0bda9 100644 --- a/man/pactl.1.xml.in +++ b/man/pactl.1.xml.in @@ -76,7 +76,7 @@ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. <option> <p><opt>list</opt> [<arg>short</arg>] [<arg>TYPE</arg>]</p> <optdesc><p>Dump all currently loaded modules, available sinks, sources, streams, etc. <arg>TYPE</arg> must be one of: - modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards. If not specified, all info is listed. If + modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers. If not specified, all info is listed. If short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc> </option> diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio index 797ec067..24d2aa1c 100644 --- a/shell-completion/bash/pulseaudio +++ b/shell-completion/bash/pulseaudio @@ -113,7 +113,7 @@ _pactl() { local comps local flags='-h --help --version -s --server= --client-name=' local list_types='short sinks sources sink-inputs source-outputs cards - modules samples clients' + modules samples clients message-handlers' local commands=(stat info list exit upload-sample play-sample remove-sample load-module unload-module move-sink-input move-source-output suspend-sink suspend-source set-card-profile set-sink-port diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio index a2817ebb..c24d0387 100644 --- a/shell-completion/zsh/_pulseaudio +++ b/shell-completion/zsh/_pulseaudio @@ -285,6 +285,7 @@ _pactl_completion() { 'clients: list connected clients' 'samples: list samples' 'cards: list available cards' + 'message-handlers: list available message-handlers' ) if ((CURRENT == 2)); then diff --git a/src/pulse/util.c b/src/pulse/util.c index 54fe7a28..c3a3320a 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -342,3 +342,82 @@ int pa_msleep(unsigned long t) { #error "Platform lacks a sleep function." #endif } + +/* Split the specified string into elements. An element is defined as + * a sub-string between curly braces. The function is needed to parse + * the parameters of messages. Each time it is called returns the position + * of the next element in start_pos and the length of the element in + * length. If max_length is not 0, it is verified that the retrieved + * element is within the bounds of the parent element. If the parameter + * element is not NULL, a newly allocated string containing the retrieved + * element is returned. The caller is responsible to free the string. + * The variable state points to, should be initialized to NULL before + * the first call. The function returns 1 on success, 0 if end of string + * or end of element is encountered and -1 on parse error. */ +int pa_split_message_parameter_string(const char *c, uint32_t max_length, const char **start_pos, uint32_t *length, char **element, const char **state) { + const char *current = *state ? *state : c; + uint32_t open_braces; + + pa_assert(start_pos); + pa_assert(length); + + *start_pos = NULL; + + /* Empty or no string */ + if (!*current || *c == 0) + return 0; + + /* Find opening brace */ + while (*current != 0) { + + if (*current == '{') + break; + + if (*current == '}') { + + /* reached end of element, same as end of string */ + if (current == c + max_length) + return 0; + + /* unexpected closing brace, parse error */ + return -1; + } + + current++; + } + + /* No opening brace found, end of string */ + if (*current == 0) + return 0; + + *start_pos = current + 1; + open_braces = 1; + + while (open_braces != 0 && *current != 0) { + current++; + if (*current == '{') + open_braces++; + if (*current == '}') + open_braces--; + } + + /* Parse error, closing brace missing */ + if (open_braces != 0) { + *start_pos = NULL; + return -1; + } + + /* Parse error, access behind end of element */ + if (max_length && (current > c + max_length)) { + *start_pos = NULL; + return -1; + } + + *state = current + 1; + *length = current - *start_pos; + + if (element) + *element = pa_xstrndup(*start_pos, *length); + + return 1; +} diff --git a/src/pulse/util.h b/src/pulse/util.h index e4a62da6..94df623f 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -22,6 +22,7 @@ ***/ #include <stddef.h> +#include <inttypes.h> #include <pulse/cdecl.h> #include <pulse/version.h> @@ -54,6 +55,9 @@ char *pa_path_get_filename(const char *p); /** Wait t milliseconds */ int pa_msleep(unsigned long t); +/** Split message parameter string */ +int pa_split_message_parameter_string(const char *c, uint32_t max_length, const char **start_pos, uint32_t *length, char **element, const char **state); + PA_C_DECL_END #endif diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 66c0f251..edb253d6 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -851,6 +851,53 @@ static void send_message_callback(pa_context *c, int success, const char *respon complete_action(); } +static void list_handlers_callback(pa_context *c, int success, const char *response, void *userdata) { + const char *state = NULL; + const char *start_pos; + uint32_t length; + int err; + + if (!success) { + pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + while ((err = pa_split_message_parameter_string(response, 0, &start_pos, &length, NULL, &state)) > 0) { + const char *state2 = NULL; + const char *pos; + char *path; + char *description; + uint32_t max_length = length; + + if (pa_split_message_parameter_string(start_pos, max_length, &pos, &length, &path, &state2) <= 0) { + err = -1; + break; + } + if (pa_split_message_parameter_string(start_pos, max_length, &pos, &length, &description, &state2) <= 0) { + pa_xfree(path); + err = -1; + break; + } + + if (short_list_format) + printf("%s\n", path); + else + printf("Message Handler %s\n Description: %s\n", path, description); + + pa_xfree(path); + pa_xfree(description); + } + + if (err < 0) { + pa_log(_("list-handlers message response could not be parsed correctly")); + quit(1); + return; + } + + complete_action(); +} + static void volume_relative_adjust(pa_cvolume *cv) { pa_assert(volume_flags & VOL_RELATIVE); @@ -1262,6 +1309,8 @@ static void context_state_callback(pa_context *c, void *userdata) { o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL); else if (pa_streq(list_type, "cards")) o = pa_context_get_card_info_list(c, get_card_info_callback, NULL); + else if (pa_streq(list_type, "message-handlers")) + o = pa_context_send_message_to_object(c, "/core", "list-handlers", NULL, list_handlers_callback, NULL); else pa_assert_not_reached(); } else { @@ -1312,6 +1361,12 @@ static void context_state_callback(pa_context *c, void *userdata) { actions++; } + o = pa_context_send_message_to_object(c, "/core", "list-handlers", NULL, list_handlers_callback, NULL); + if (o) { + pa_operation_unref(o); + actions++; + } + o = NULL; } break; @@ -1698,12 +1753,13 @@ int main(int argc, char *argv[]) { if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") || pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") || pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") || - pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) { + pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") || + pa_streq(argv[i], "message-handlers")) { list_type = pa_xstrdup(argv[i]); } else if (pa_streq(argv[i], "short")) { short_list_format = true; } else { - pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards"); + pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers"); goto quit; } } -- 2.14.1