diff --git a/PROTOCOL b/PROTOCOL index 4307898c2..72d3af3c0 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -435,6 +435,23 @@ sink, source and card ports): string availability_group uint32 type +## v35, implemented by >= 15.0 + +Added new command for communication with objects. + +PA_COMMAND_SEND_OBJECT_MESSAGE: +sends a message to an object identified by an object path + +parameters: + string object_path - unique path identifying the object + string message - message name + string message_parameters - additional parameters if required (may be + NULL, which should be treated the same as an + empty string) + +The command returns a string, which may be empty or NULL (NULL should be +treated the same as an empty 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 6e6049fec..fb1b518e9 100644 --- a/configure.ac +++ b/configure.ac @@ -42,7 +42,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, 34) +AC_SUBST(PA_PROTOCOL_VERSION, 35) # The stable ABI for client applications, for the version info x:y:z # always will hold x=z diff --git a/meson.build b/meson.build index 683f40e1f..06c44d748 100644 --- a/meson.build +++ b/meson.build @@ -19,7 +19,7 @@ endif pa_version_major_minor = pa_version_major + '.' + pa_version_minor pa_api_version = 12 -pa_protocol_version = 34 +pa_protocol_version = 35 # The stable ABI for client applications, for the version info x:y:z # always will hold x=z diff --git a/src/map-file b/src/map-file index b0cd1bfc4..ea3f70268 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 3027f38ed..7fefb9ce0 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -2205,3 +2205,67 @@ pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in return o; } + +/** Object response string processing **/ + +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 = 1; + + 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 = 0; + response = ""; + } else if (pa_tagstruct_gets(t, &response) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (!response) + response = ""; + + 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 *object_path, 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, object_path); + 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 c547bde09..fae44a7d1 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 @@ -489,6 +495,16 @@ 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); + +/** Send a message to an object that registered a message handler. \since 15.0 */ +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 */ /** Stores information about clients. Please note that this structure diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 70338b9f3..3de960def 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 v34 (14.0) */ + PA_COMMAND_SEND_OBJECT_MESSAGE, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c index ab632a5ab..c2d26e63e 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 v35 (15.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 e8559b239..f8dad57d6 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -4721,6 +4722,55 @@ 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 *object_path = 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, &object_path) < 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, object_path != NULL, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_utf8_valid(object_path), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, message != NULL, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_utf8_valid(message), tag, PA_ERR_INVALID); + if (message_parameters) + CHECK_VALIDITY(c->pstream, pa_utf8_valid(message_parameters), tag, PA_ERR_INVALID); + + client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)); + pa_log_debug("Client %s sent message %s to path %s", client_name, message, object_path); + if (message_parameters) + pa_log_debug("Message parameters: %s", message_parameters); + + ret = pa_message_handler_send_message(c->protocol->core, object_path, message, message_parameters, &response); + + if (ret < 0) { + pa_pstream_send_error(c->pstream, tag, -ret); + return; + } + + reply = reply_new(tag); + pa_tagstruct_puts(reply, 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; @@ -4972,6 +5022,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 };