This patch adds a new feature to the core which allows to exchange messages between objects. An object can register/unregister a message handler with pa_core_{register, unregister}_message_handler() while any other object can check if a handler is registered for some name with pa_core_find_message_handler() and then call the returned handler. The patch is a precondition for the following patches that also allow clients to send messages to pulseaudio objects. There is no restriction on object names, but the intention is to use a path-like syntax, for example /core/sink_1 for a sink or /name/instances/index for modules. The exact naming convention still needs to be agreed. Objects also can have a short name which can be used in several ways. First objects of the same type should have the same short name, so that you can either send a message to all instances or to the first instance of an object. pa_core_find_message_handler() returns the first instance if a short name is specified as search string. Second, special objects like the default sink/source can have unique short names to make it easy to send messages to them. --- src/pulsecore/core-util.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/core-util.h | 22 +++++++++++++++ src/pulsecore/core.c | 4 +++ src/pulsecore/core.h | 2 +- 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index d4cfa20c..2aa0e2e4 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -3747,3 +3747,74 @@ size_t pa_page_size(void) { return 4096; #endif } + +/* Message handler registration functions */ + +/* This function finds a message handler for a given name or short name. name must be a + * unique name, while short name should be equal for objects of the same type. It can be + * used to retrieve the message handler of the first instance of an object type. The + * short name can also be used to refer to special objects like a default sink. */ +int pa_core_find_message_handler(pa_core *c, const char *search_string, pa_core_message_handler **handler) { + void *state = NULL; + + pa_assert(c); + pa_assert(search_string); + + /* First check if the search string matches a handler name */ + *handler = pa_hashmap_get(c->message_handlers, search_string); + + if (!*handler) { + /* Search string was not a handler name, look for matching short name */ + while ((*handler = pa_hashmap_iterate(c->message_handlers, &state, NULL))) { + if (pa_safe_streq((const char *) (*handler)->short_name, search_string)) + break; + } + + /* Nothing found, return failure */ + if (!*handler) + return -1; + } + + return 0; +} + +/* Register message handler. recipient_name must be a unique name. short_name can be used + * to group objects of the same type, see pa_core_find_message_handler(). short_name and + * description are optional and may be NULL. */ +int pa_core_register_message_handler(pa_core *c, const char *recipient_name, const char *short_name, const char *description, pa_core_message_handler_cb_t cb, void *userdata) { + pa_core_message_handler *handler; + + pa_assert(c); + pa_assert(recipient_name); + pa_assert(cb); + pa_assert(userdata); + + if (pa_core_find_message_handler(c, recipient_name, &handler) > 0) { + pa_log_warn("Cannot register new message handler, name %s is already registered.", recipient_name); + return -1; + } + + handler = pa_xnew0(pa_core_message_handler, 1); + handler->userdata = userdata; + handler->callback = cb; + handler->recipient = pa_xstrdup(recipient_name); + handler->short_name = pa_xstrdup(short_name); + handler->description = pa_xstrdup(description); + + pa_assert_se(pa_hashmap_put(c->message_handlers, handler->recipient, (void *) handler) == 0); + return 0; +} + +/* Unregister a message handler */ +void pa_core_unregister_message_handler(pa_core *c, const char *recipient_name) { + pa_core_message_handler *handler; + + pa_assert(c); + pa_assert(recipient_name); + + pa_assert_se(handler = pa_hashmap_remove(c->message_handlers, recipient_name)); + pa_xfree(handler->recipient); + pa_xfree(handler->short_name); + pa_xfree(handler->description); + pa_xfree(handler); +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index e28b6aa7..33d85e04 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -34,6 +34,7 @@ #include <pulse/gccmacro.h> #include <pulse/volume.h> +#include <pulsecore/core.h> #include <pulsecore/i18n.h> #include <pulsecore/macro.h> #include <pulsecore/socket.h> @@ -318,4 +319,25 @@ static inline size_t PA_PAGE_ALIGN(size_t l) { return (l + page_size - 1) & ~(page_size - 1); } +/* Message handler types and functions */ + +typedef int (*pa_core_message_handler_cb_t)( + const char *recipient, + const char *message, + const char *message_parameters, + char **response, + void *userdata); + +typedef struct pa_core_message_handler { + char *recipient; + char *short_name; + char *description; + pa_core_message_handler_cb_t callback; + void *userdata; +} pa_core_message_handler; + +int pa_core_register_message_handler(pa_core *c, const char *recipient_name, const char *short_name, const char *description, pa_core_message_handler_cb_t cb, void *userdata); +void pa_core_unregister_message_handler(pa_core *c, const char *recipient_name); +int pa_core_find_message_handler(pa_core *c, const char *search_string, pa_core_message_handler **handler); + #endif diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index e01677d5..3145caaf 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -103,6 +103,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); c->default_source = NULL; c->default_sink = NULL; @@ -204,6 +205,9 @@ static void core_free(pa_object *o) { pa_assert(pa_hashmap_isempty(c->shared)); pa_hashmap_free(c->shared); + pa_assert(pa_hashmap_isempty(c->message_handlers)); + pa_hashmap_free(c->message_handlers); + pa_assert(pa_hashmap_isempty(c->modules_pending_unload)); pa_hashmap_free(c->modules_pending_unload); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 79a095d2..bc8a371e 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -160,7 +160,7 @@ struct pa_core { pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache; /* Some hashmaps for all sorts of entities */ - pa_hashmap *namereg, *shared; + pa_hashmap *namereg, *shared, *message_handlers; /* The default sink/source as configured by the user. If the user hasn't * explicitly configured anything, these are set to NULL. These are strings -- 2.11.0