This patch adds the PA_COMMAND_SEND_OBJECT_MESSAGE command to protocol-native so that clients can use the messaging feature introduced in patch "core: add generic message interface". Sending messages can in effect replace the extension system for modules. The approach is more flexible than the extension interface because a generic string format is used to exchange information. Furthermore the messaging system can be used for any object, not only for modules, and is easier to implement than extensions. --- PROTOCOL | 7 +++++ configure.ac | 2 +- src/map-file | 1 + src/pulse/introspect.c | 59 +++++++++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 7 +++++ src/pulsecore/native-common.h | 2 ++ src/pulsecore/pdispatch.c | 2 ++ src/pulsecore/protocol-native.c | 44 ++++++++++++++++++++++++++++++ 8 files changed, 123 insertions(+), 1 deletion(-) diff --git a/PROTOCOL b/PROTOCOL index e11aaa01..5cf95364 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -425,6 +425,13 @@ Check commit 451d1d676237c81 for further details. Added signal subscription functions. Clients can now subscribe to signals that PulseAudio sends. +## v34, implemented by > 11.0 + +Added new command for communication with objects. + +PA_COMMAND_SEND_OBJECT_MESSAGE: +sends a message to an object that registered a named message handler + #### 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 e28755b0..011b45eb 100644 --- a/configure.ac +++ b/configure.ac @@ -40,7 +40,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, 33) +AC_SUBST(PA_PROTOCOL_VERSION, 34) # 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 d6cc06c1..83301eba 100644 --- a/src/map-file +++ b/src/map-file @@ -120,6 +120,7 @@ pa_context_suspend_sink_by_name; pa_context_suspend_source_by_index; pa_context_suspend_source_by_name; pa_context_unload_module; +pa_context_send_message_to_object; pa_context_unref; pa_cvolume_avg; pa_cvolume_avg_mask; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 510d784a..f23ff932 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -2184,3 +2184,62 @@ pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in return o; } + +/** Object response string **/ + +static void context_string_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + const char *response; + + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t, false) < 0) + goto finish; + + response = NULL; + } else if (pa_tagstruct_gets(t, &response) || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback; + cb(o->context, response, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_send_message_to_object(pa_context *c, const char *recipient_name, const char *message, const char *message_parameters, pa_context_string_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + 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); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SEND_OBJECT_MESSAGE, &tag); + + pa_tagstruct_puts(t, recipient_name); + pa_tagstruct_puts(t, message); + pa_tagstruct_puts(t, message_parameters); + + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_string_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 43389b73..e78f6665 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -423,6 +423,9 @@ typedef struct pa_module_info { /** Callback prototype for pa_context_get_module_info() and friends */ typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata); +/** Callback prototype for pa_context_send_message_to_object() */ +typedef void (*pa_context_string_cb_t)(pa_context *c, const char *response, void *userdata); + /** Get some information about a module by its index */ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata); @@ -438,6 +441,10 @@ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char /** Unload a module. */ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); +/** Send a message to an object. This does not only apply to modules but to any object + * that registered a message handler. */ +pa_operation* pa_context_send_message_to_object(pa_context *c, const char *recipient_name, const char *message, const char *message_parameters, pa_context_string_cb_t cb, void *userdata); + /** @} */ /** @{ \name Clients */ diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 5b9d5f0c..06c7c576 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -188,6 +188,8 @@ enum { * BOTH DIRECTIONS */ PA_COMMAND_REGISTER_MEMFD_SHMID, + PA_COMMAND_SEND_OBJECT_MESSAGE, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index f5aa5a24..22ea90c5 100644 --- a/src/pulsecore/pdispatch.c +++ b/src/pulsecore/pdispatch.c @@ -200,6 +200,8 @@ static const char *command_names[PA_COMMAND_MAX] = { /* Supported since protocol v31 (9.0) */ /* BOTH DIRECTIONS */ [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID", + + [PA_COMMAND_SEND_OBJECT_MESSAGE] = "SEND_OBJECT_MESSAGE", }; #endif diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index e31562cf..6f6c50f7 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -4707,6 +4707,48 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, protocol_error(c); } +/* Send message to an object which registered a handler. Result must be returned as string. */ +static void command_send_object_message(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); + const char *recipient_name = NULL; + const char *message = NULL; + const char *message_parameters = NULL; + char *response = NULL; + pa_tagstruct *reply; + + pa_native_connection_assert_ref(c); + pa_assert(t); + + if (pa_tagstruct_gets(t, &recipient_name) < 0 || + pa_tagstruct_gets(t, &message) < 0 || + pa_tagstruct_gets(t, &message_parameters) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + CHECK_VALIDITY(c->pstream, pa_utf8_valid(recipient_name), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, message != NULL, tag, PA_ERR_INVALID); + + if (pa_core_message_handler_test(c->protocol->core, recipient_name) != PA_CORE_HANDLER_PUBLIC) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + return; + } + + if (pa_core_send_message(c->protocol->core, recipient_name, message, message_parameters, NULL, &response) != PA_CORE_SEND_OK) { + pa_pstream_send_error(c->pstream, tag, 1); + pa_xfree(response); + return; + } + + reply = reply_new(tag); + pa_tagstruct_puts(reply, (const char *)response); + pa_xfree(response); + + pa_pstream_send_tagstruct(c->pstream, reply); +} + static void command_set_card_profile(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 = PA_INVALID_INDEX; @@ -4959,6 +5001,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid, + [PA_COMMAND_SEND_OBJECT_MESSAGE] = command_send_object_message, + [PA_COMMAND_EXTENSION] = command_extension }; -- 2.11.0