diff options
-rw-r--r-- | doxygen/doxygen.conf.in | 1 | ||||
-rw-r--r-- | man/pactl.1.xml.in | 4 | ||||
-rw-r--r-- | shell-completion/bash/pulseaudio | 2 | ||||
-rw-r--r-- | shell-completion/zsh/_pulseaudio | 1 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/map-file | 2 | ||||
-rw-r--r-- | src/pulse/introspect.c | 11 | ||||
-rw-r--r-- | src/pulse/introspect.h | 2 | ||||
-rw-r--r-- | src/pulse/meson.build | 2 | ||||
-rw-r--r-- | src/pulse/message-params.c | 118 | ||||
-rw-r--r-- | src/pulse/message-params.h | 42 | ||||
-rw-r--r-- | src/pulsecore/core.c | 2 | ||||
-rw-r--r-- | src/pulsecore/message-handler.c | 9 | ||||
-rw-r--r-- | src/pulsecore/message-handler.h | 2 | ||||
-rw-r--r-- | src/utils/pactl.c | 66 |
15 files changed, 253 insertions, 12 deletions
diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in index a97c16543..c32543413 100644 --- a/doxygen/doxygen.conf.in +++ b/doxygen/doxygen.conf.in @@ -683,6 +683,7 @@ INPUT = @top_srcdir@/src/pulse/channelmap.h \ @top_srcdir@/src/pulse/mainloop-api.h \ @top_srcdir@/src/pulse/mainloop-signal.h \ @top_srcdir@/src/pulse/mainloop.h \ + @top_srcdir@/src/pulse/message-params.h \ @top_srcdir@/src/pulse/operation.h \ @top_srcdir@/src/pulse/proplist.h \ @top_srcdir@/src/pulse/pulseaudio.h \ diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in index 9f4b7abd3..1f094df42 100644 --- a/man/pactl.1.xml.in +++ b/man/pactl.1.xml.in @@ -80,8 +80,8 @@ 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 - short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc> + modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers. If not specified, all info is listed + with the exception of the message-handlers. If short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc> </option> <option> diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio index 1ca0e40b8..6c2635f65 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-default-sink diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio index 7668386fc..2a1f18eaa 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/Makefile.am b/src/Makefile.am index d298d85d3..19f100f11 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -929,6 +929,7 @@ libpulse_la_SOURCES = \ pulse/timeval.c pulse/timeval.h \ pulse/utf8.c pulse/utf8.h \ pulse/util.c pulse/util.h \ + pulse/message-params.c pulse/message-params.h \ pulse/volume.c pulse/volume.h \ pulse/xmalloc.c pulse/xmalloc.h diff --git a/src/map-file b/src/map-file index ea3f70268..0acbf05b5 100644 --- a/src/map-file +++ b/src/map-file @@ -229,6 +229,8 @@ pa_mainloop_quit; pa_mainloop_run; pa_mainloop_set_poll_func; pa_mainloop_wakeup; +pa_message_params_read_raw; +pa_message_params_read_string; pa_msleep; pa_thread_make_realtime; pa_operation_cancel; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 7fefb9ce0..0671fa163 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -2236,8 +2236,15 @@ static void context_string_callback(pa_pdispatch *pd, uint32_t command, uint32_t response = ""; if (o->callback) { - pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback; - cb(o->context, success, response, o->userdata); + char *response_copy; + pa_context_string_cb_t cb; + + response_copy = pa_xstrdup(response); + + cb = (pa_context_string_cb_t) o->callback; + cb(o->context, success, response_copy, o->userdata); + + pa_xfree(response_copy); } finish: diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index d8cdc3c8c..2c3c4acf4 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -498,7 +498,7 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s /** @{ \name Messages */ /** Callback prototype for pa_context_send_message_to_object() \since 15.0 */ -typedef void (*pa_context_string_cb_t)(pa_context *c, int success, const char *response, void *userdata); +typedef void (*pa_context_string_cb_t)(pa_context *c, int success, char *response, void *userdata); /** Send a message to an object that registered a message handler. For more information * see https://cgit.freedesktop.org/pulseaudio/pulseaudio/tree/doc/messaging_api.txt. \since 15.0 */ diff --git a/src/pulse/meson.build b/src/pulse/meson.build index aaebff53e..abc1e7921 100644 --- a/src/pulse/meson.build +++ b/src/pulse/meson.build @@ -19,6 +19,7 @@ libpulse_sources = [ 'mainloop-api.c', 'mainloop-signal.c', 'mainloop.c', + 'message-params.c', 'operation.c', 'proplist.c', 'rtclock.c', @@ -50,6 +51,7 @@ libpulse_headers = [ 'mainloop-api.h', 'mainloop-signal.h', 'mainloop.h', + 'message-params.h', 'operation.h', 'proplist.h', 'pulseaudio.h', diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c new file mode 100644 index 000000000..0afda4f38 --- /dev/null +++ b/src/pulse/message-params.c @@ -0,0 +1,118 @@ +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <pulse/xmalloc.h> + +#include <pulsecore/macro.h> + +#include "message-params.h" + +/* 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 it returns the + * position of the current element in result and the state pointer is + * advanced to the next list element. + * + * 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 + * is encountered and -1 on parse error. + * + * result is set to NULL on end of string or parse error. */ +static int split_list(char *c, char **result, void **state) { + char *current = *state ? *state : c; + uint32_t open_braces; + + pa_assert(result); + + *result = NULL; + + /* Empty or no string */ + if (!current || *current == 0) + return 0; + + /* Find opening brace */ + while (*current != 0) { + + if (*current == '{') + break; + + /* unexpected closing brace, parse error */ + if (*current == '}') + return -1; + + current++; + } + + /* No opening brace found, end of string */ + if (*current == 0) + return 0; + + *result = 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) { + *result = NULL; + return -1; + } + + /* Replace } with 0 */ + *current = 0; + + *state = current + 1; + + return 1; +} + +/* Read a string from the parameter list. The state pointer is + * advanced to the next element of the list. Returns a pointer + * to a sub-string within c. The result must not be freed. */ +int pa_message_params_read_string(char *c, const char **result, void **state) { + char *start_pos; + int r; + + pa_assert(result); + + if ((r = split_list(c, &start_pos, state)) == 1) + *result = start_pos; + + return r; +} + +/* Another wrapper for split_list() to distinguish between reading + * pure string data and raw data which may contain further lists. */ +int pa_message_params_read_raw(char *c, char **result, void **state) { + return split_list(c, result, state); +} diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h new file mode 100644 index 000000000..5c9bc1a6e --- /dev/null +++ b/src/pulse/message-params.h @@ -0,0 +1,42 @@ +#ifndef foomessagehelperhfoo +#define foomessagehelperhfoo + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stddef.h> +#include <stdbool.h> +#include <inttypes.h> + +#include <pulse/cdecl.h> +#include <pulse/version.h> + +/** \file + * Utility functions for reading and writing message parameters */ + +PA_C_DECL_BEGIN + +/** Read raw data from a parameter list. Used to split a message parameter + * string into list elements \since 15.0 */ +int pa_message_params_read_raw(char *c, char **result, void **state); + +/** Read a string from a parameter list. \since 15.0 */ +int pa_message_params_read_string(char *c, const char **result, void **state); + +PA_C_DECL_END + +#endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index cb824ed17..da8b3b3da 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -86,7 +86,7 @@ static char *message_handler_list(pa_core *c) { return pa_strbuf_to_string_free(buf); } -static int core_message_handler(const char *object_path, const char *message, const char *message_parameters, char **response, void *userdata) { +static int core_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) { pa_core *c; pa_assert(c = (pa_core *) userdata); diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c index 18c62fc2d..427186dbd 100644 --- a/src/pulsecore/message-handler.c +++ b/src/pulsecore/message-handler.c @@ -90,6 +90,8 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) { /* Send a message to an object identified by object_path */ int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) { struct pa_message_handler *handler; + int ret; + char *parameter_copy; pa_assert(c); pa_assert(object_path); @@ -101,9 +103,14 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c if (!(handler = pa_hashmap_get(c->message_handlers, object_path))) return -PA_ERR_NOENTITY; + parameter_copy = pa_xstrdup(message_parameters); + /* The handler is expected to return an error code and may also return an error string in response */ - return handler->callback(handler->object_path, message, message_parameters, response, handler->userdata); + ret = handler->callback(handler->object_path, message, parameter_copy, response, handler->userdata); + + pa_xfree(parameter_copy); + return ret; } /* Set handler description */ diff --git a/src/pulsecore/message-handler.h b/src/pulsecore/message-handler.h index be94510f0..38b24e1b2 100644 --- a/src/pulsecore/message-handler.h +++ b/src/pulsecore/message-handler.h @@ -26,7 +26,7 @@ typedef int (*pa_message_handler_cb_t)( const char *object_path, const char *message, - const char *message_parameters, + char *message_parameters, char **response, void *userdata); diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 26b85dfc3..bc1c5265a 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -35,6 +35,7 @@ #include <sndfile.h> #include <pulse/pulseaudio.h> +#include <pulse/message-params.h> #include <pulse/ext-device-restore.h> #include <pulsecore/i18n.h> @@ -882,7 +883,7 @@ static void index_callback(pa_context *c, uint32_t idx, void *userdata) { complete_action(); } -static void send_message_callback(pa_context *c, int success, const char *response, void *userdata) { +static void send_message_callback(pa_context *c, int success, char *response, void *userdata) { if (!success) { pa_log(_("Send message failed: %s"), pa_strerror(pa_context_errno(c))); @@ -895,6 +896,62 @@ 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, char *response, void *userdata) { + void *state = NULL; + char *handler_list; + char *handler_struct; + int err; + + if (!success) { + pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (pa_message_params_read_raw(response, &handler_list, &state) <= 0) { + pa_log(_("list-handlers message response could not be parsed correctly")); + quit(1); + return; + } + + state = NULL; + while ((err = pa_message_params_read_raw(handler_list, &handler_struct, &state)) > 0) { + void *state2 = NULL; + const char *path; + const char *description; + + if (pa_message_params_read_string(handler_struct, &path, &state2) <= 0) { + err = -1; + break; + } + if (pa_message_params_read_string(handler_struct, &description, &state2) <= 0) { + err = -1; + break; + } + + if (short_list_format) + printf("%s\n", path); + else { + if (nl) + printf("\n"); + nl = true; + + printf("Message Handler %s\n" + "\tDescription: %s\n", + path, + 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); @@ -1308,6 +1365,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 { @@ -1744,12 +1803,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; } } |