On Sun, 2017-10-29 at 20:51 +0100, Georg Chini wrote: > This patch adds the PA_COMMAND_SEND_OBJECT_MESSAGE command to protocol-native > so that clients can use the messaging feature introduced in the previous patch. > > 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 | 14 ++++++++++ > configure.ac | 2 +- > src/map-file | 1 + > src/pulse/introspect.c | 61 +++++++++++++++++++++++++++++++++++++++++ > src/pulse/introspect.h | 16 +++++++++++ > src/pulsecore/native-common.h | 3 ++ > src/pulsecore/pdispatch.c | 3 ++ > src/pulsecore/protocol-native.c | 51 ++++++++++++++++++++++++++++++++++ > 8 files changed, 150 insertions(+), 1 deletion(-) > > diff --git a/PROTOCOL b/PROTOCOL > index 546998b7..b57edea5 100644 > --- a/PROTOCOL > +++ b/PROTOCOL > @@ -420,6 +420,20 @@ memfd support only to 10.0+ clients. > > Check commit 451d1d676237c81 for further details. > > +## v33, implemented by >= 12.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 > + > +parameters: > + const char *recipient_name - name of message handler > + const char *message - message command > + const char *message_parameters - additional parameters if required s/const char */string / (to be consistent with the v26 section) > + > +The command returns a string. > + > #### 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 0c38fbb5..cd62a477 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, 32) > +AC_SUBST(PA_PROTOCOL_VERSION, 33) > > # 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 93a62b86..9f6181f1 100644 > --- a/src/map-file > +++ b/src/map-file > @@ -87,6 +87,7 @@ pa_context_remove_autoload_by_name; > pa_context_remove_sample; > pa_context_rttime_new; > pa_context_rttime_restart; > +pa_context_send_message_to_object; > pa_context_set_card_profile_by_index; > pa_context_set_card_profile_by_name; > pa_context_set_default_sink; > diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c > index 510d784a..fb64150b 100644 > --- a/src/pulse/introspect.c > +++ b/src/pulse/introspect.c > @@ -2184,3 +2184,64 @@ 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; > + int success = 0; > + > + 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; > + > + success = -1; > + response = NULL; > + } else if (pa_tagstruct_gets(t, &response) < 0 || > + !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, success, 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..e46e66e0 100644 > --- a/src/pulse/introspect.h > +++ b/src/pulse/introspect.h > @@ -204,6 +204,12 @@ > * Server modules can be remotely loaded and unloaded using > * pa_context_load_module() and pa_context_unload_module(). > * > + * \subsection message_subsec Messages > + * > + * Server objects like sinks, sink inputs or modules can register a message > + * handler to communicate with clients. A message can be sent to a named > + * message handler using pa_context_send_message_to_object(). > + * > * \subsection client_subsec Clients > * > * The only operation supported on clients is the possibility of kicking > @@ -423,6 +429,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, int success, const char *response, void *userdata); > + This would fit better in the "Messages" section. > /** 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); > > @@ -440,6 +449,13 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s > > /** @} */ > > +/** @{ \name Messages */ > + > +/** Send a message to an 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); There should be pointer to the message API documentation. > + > +/** @} */ > + > /** @{ \name Clients */ > > /** Stores information about clients. Please note that this structure > diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h > index 70338b9f..5c3cf521 100644 > --- a/src/pulsecore/native-common.h > +++ b/src/pulsecore/native-common.h > @@ -187,6 +187,9 @@ enum { > * BOTH DIRECTIONS */ > PA_COMMAND_REGISTER_MEMFD_SHMID, > > + /* Supported since protocol v33 (12.0) */ > + PA_COMMAND_SEND_OBJECT_MESSAGE, > + > PA_COMMAND_MAX > }; > > diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c > index ab632a5a..d317a15e 100644 > --- a/src/pulsecore/pdispatch.c > +++ b/src/pulsecore/pdispatch.c > @@ -199,6 +199,9 @@ static const char *command_names[PA_COMMAND_MAX] = { > /* Supported since protocol v31 (9.0) */ > /* BOTH DIRECTIONS */ > [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID", > + > + /* Supported since protocol v33 (12.0) */ > + [PA_COMMAND_SEND_OBJECT_MESSAGE] = "SEND_OBJECT_MESSAGE", > }; > > #endif > diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c > index 6ff5ed40..76905207 100644 > --- a/src/pulsecore/protocol-native.c > +++ b/src/pulsecore/protocol-native.c > @@ -47,6 +47,7 @@ > #include <pulsecore/namereg.h> > #include <pulsecore/core-scache.h> > #include <pulsecore/core-subscribe.h> > +#include <pulsecore/message-handler.h> > #include <pulsecore/log.h> > #include <pulsecore/mem.h> > #include <pulsecore/strlist.h> > @@ -4685,6 +4686,54 @@ 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; > + const char *client_name; > + char *response = NULL; > + int ret; > + 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); You need to check that recipient_name isn't NULL before calling pa_utf8_valid(). -- Tanu https://www.patreon.com/tanuk