153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Written by David Henningsson <david.henningsson@canonical.com> 553a5a1b3Sopenharmony_ci Copyright 2010 Canonical Ltd. 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci Some code taken from other parts of PulseAudio, these are 853a5a1b3Sopenharmony_ci Copyright 2006-2009 Lennart Poettering 953a5a1b3Sopenharmony_ci 1053a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 1153a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 1253a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1353a5a1b3Sopenharmony_ci or (at your option) any later version. 1453a5a1b3Sopenharmony_ci 1553a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1653a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1753a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1853a5a1b3Sopenharmony_ci General Public License for more details. 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 2153a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 2253a5a1b3Sopenharmony_ci***/ 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2553a5a1b3Sopenharmony_ci#include <config.h> 2653a5a1b3Sopenharmony_ci#endif 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <pulse/proplist.h> 2953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3253a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 3353a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3453a5a1b3Sopenharmony_ci#include <pulsecore/dbus-shared.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/strbuf.h> 3653a5a1b3Sopenharmony_ci 3753a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("David Henningsson"); 3853a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started"); 3953a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 4053a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 4153a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 4253a5a1b3Sopenharmony_ci "channels=<number of channels> " 4353a5a1b3Sopenharmony_ci "sink_name=<name for the sink> " 4453a5a1b3Sopenharmony_ci "sink_properties=<properties for the card> " 4553a5a1b3Sopenharmony_ci "sink_client_name=<jack client name> " 4653a5a1b3Sopenharmony_ci "sink_channels=<number of channels> " 4753a5a1b3Sopenharmony_ci "sink_channel_map=<channel map> " 4853a5a1b3Sopenharmony_ci "source_name=<name for the source> " 4953a5a1b3Sopenharmony_ci "source_properties=<properties for the source> " 5053a5a1b3Sopenharmony_ci "source_client_name=<jack client name> " 5153a5a1b3Sopenharmony_ci "source_channels=<number of channels> " 5253a5a1b3Sopenharmony_ci "source_channel_map=<channel map> " 5353a5a1b3Sopenharmony_ci "connect=<connect ports?>"); 5453a5a1b3Sopenharmony_ci 5553a5a1b3Sopenharmony_ci#define JACK_SERVICE_NAME "org.jackaudio.service" 5653a5a1b3Sopenharmony_ci#define JACK_INTERFACE_NAME "org.jackaudio.JackControl" 5753a5a1b3Sopenharmony_ci#define JACK_INTERFACE_PATH "/org/jackaudio/Controller" 5853a5a1b3Sopenharmony_ci 5953a5a1b3Sopenharmony_ci#define SERVICE_FILTER \ 6053a5a1b3Sopenharmony_ci "type='signal'," \ 6153a5a1b3Sopenharmony_ci "sender='" DBUS_SERVICE_DBUS "'," \ 6253a5a1b3Sopenharmony_ci "interface='" DBUS_INTERFACE_DBUS "'," \ 6353a5a1b3Sopenharmony_ci "member='NameOwnerChanged'," \ 6453a5a1b3Sopenharmony_ci "arg0='" JACK_SERVICE_NAME "'" 6553a5a1b3Sopenharmony_ci 6653a5a1b3Sopenharmony_ci#define RUNNING_FILTER(_a) \ 6753a5a1b3Sopenharmony_ci "type='signal'," \ 6853a5a1b3Sopenharmony_ci "sender='" JACK_SERVICE_NAME "'," \ 6953a5a1b3Sopenharmony_ci "interface='" JACK_INTERFACE_NAME "'," \ 7053a5a1b3Sopenharmony_ci "member='" _a "'" 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 7353a5a1b3Sopenharmony_ci "channels", 7453a5a1b3Sopenharmony_ci "sink_enabled", 7553a5a1b3Sopenharmony_ci "sink_name", 7653a5a1b3Sopenharmony_ci "sink_properties", 7753a5a1b3Sopenharmony_ci "sink_client_name", 7853a5a1b3Sopenharmony_ci "sink_channels", 7953a5a1b3Sopenharmony_ci "sink_channel_map", 8053a5a1b3Sopenharmony_ci "source_enabled", 8153a5a1b3Sopenharmony_ci "source_name", 8253a5a1b3Sopenharmony_ci "source_properties", 8353a5a1b3Sopenharmony_ci "source_client_name", 8453a5a1b3Sopenharmony_ci "source_channels", 8553a5a1b3Sopenharmony_ci "source_channel_map", 8653a5a1b3Sopenharmony_ci "connect", 8753a5a1b3Sopenharmony_ci NULL 8853a5a1b3Sopenharmony_ci}; 8953a5a1b3Sopenharmony_ci 9053a5a1b3Sopenharmony_ci#define JACK_SS_SINK 0 9153a5a1b3Sopenharmony_ci#define JACK_SS_SOURCE 1 9253a5a1b3Sopenharmony_ci#define JACK_SS_COUNT 2 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_cistatic const char* const modnames[JACK_SS_COUNT] = { 9553a5a1b3Sopenharmony_ci "module-jack-sink", 9653a5a1b3Sopenharmony_ci "module-jack-source" 9753a5a1b3Sopenharmony_ci}; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_cistatic const char* const modtypes[JACK_SS_COUNT] = { 10053a5a1b3Sopenharmony_ci "sink", 10153a5a1b3Sopenharmony_ci "source" 10253a5a1b3Sopenharmony_ci}; 10353a5a1b3Sopenharmony_ci 10453a5a1b3Sopenharmony_cistruct moddata { 10553a5a1b3Sopenharmony_ci bool enabled; 10653a5a1b3Sopenharmony_ci char *name; 10753a5a1b3Sopenharmony_ci pa_proplist *proplist; 10853a5a1b3Sopenharmony_ci char *client_name; 10953a5a1b3Sopenharmony_ci uint32_t channels; 11053a5a1b3Sopenharmony_ci pa_channel_map channel_map; 11153a5a1b3Sopenharmony_ci}; 11253a5a1b3Sopenharmony_ci 11353a5a1b3Sopenharmony_cistruct userdata { 11453a5a1b3Sopenharmony_ci pa_module *module; 11553a5a1b3Sopenharmony_ci pa_core *core; 11653a5a1b3Sopenharmony_ci pa_dbus_connection *connection; 11753a5a1b3Sopenharmony_ci bool filter_added, match_added; 11853a5a1b3Sopenharmony_ci bool is_service_started; 11953a5a1b3Sopenharmony_ci bool autoconnect_ports; 12053a5a1b3Sopenharmony_ci struct moddata mod_args[JACK_SS_COUNT]; 12153a5a1b3Sopenharmony_ci /* Using index here protects us from module unloading without us knowing */ 12253a5a1b3Sopenharmony_ci int jack_module_index[JACK_SS_COUNT]; 12353a5a1b3Sopenharmony_ci}; 12453a5a1b3Sopenharmony_ci 12553a5a1b3Sopenharmony_cistatic void ensure_ports_stopped(struct userdata* u) { 12653a5a1b3Sopenharmony_ci unsigned i; 12753a5a1b3Sopenharmony_ci pa_assert(u); 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_ci for (i = 0; i < JACK_SS_COUNT; i++) 13053a5a1b3Sopenharmony_ci if (u->jack_module_index[i]) { 13153a5a1b3Sopenharmony_ci pa_module_unload_request_by_index(u->core, u->jack_module_index[i], true); 13253a5a1b3Sopenharmony_ci u->jack_module_index[i] = 0; 13353a5a1b3Sopenharmony_ci pa_log_info("Stopped %s.", modnames[i]); 13453a5a1b3Sopenharmony_ci } 13553a5a1b3Sopenharmony_ci} 13653a5a1b3Sopenharmony_ci 13753a5a1b3Sopenharmony_cistatic char* proplist_to_arg(pa_proplist *p) { 13853a5a1b3Sopenharmony_ci const char *key; 13953a5a1b3Sopenharmony_ci void *state = NULL; 14053a5a1b3Sopenharmony_ci pa_strbuf *buf; 14153a5a1b3Sopenharmony_ci 14253a5a1b3Sopenharmony_ci pa_assert(p); 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci buf = pa_strbuf_new(); 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci while ((key = pa_proplist_iterate(p, &state))) { 14753a5a1b3Sopenharmony_ci const char *v; 14853a5a1b3Sopenharmony_ci char *escaped; 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci if (!pa_strbuf_isempty(buf)) 15153a5a1b3Sopenharmony_ci pa_strbuf_puts(buf, " "); 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci if ((v = pa_proplist_gets(p, key))) { 15453a5a1b3Sopenharmony_ci pa_strbuf_printf(buf, "%s=\"", key); 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci escaped = pa_escape(v, "\"'"); 15753a5a1b3Sopenharmony_ci pa_strbuf_puts(buf, escaped); 15853a5a1b3Sopenharmony_ci pa_xfree(escaped); 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci pa_strbuf_puts(buf, "\""); 16153a5a1b3Sopenharmony_ci } else { 16253a5a1b3Sopenharmony_ci const void *value; 16353a5a1b3Sopenharmony_ci size_t nbytes; 16453a5a1b3Sopenharmony_ci char *c; 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0); 16753a5a1b3Sopenharmony_ci c = pa_xmalloc(nbytes*2+1); 16853a5a1b3Sopenharmony_ci pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1); 16953a5a1b3Sopenharmony_ci 17053a5a1b3Sopenharmony_ci pa_strbuf_printf(buf, "%s=hex:%s", key, c); 17153a5a1b3Sopenharmony_ci pa_xfree(c); 17253a5a1b3Sopenharmony_ci } 17353a5a1b3Sopenharmony_ci } 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_ci return pa_strbuf_to_string_free(buf); 17653a5a1b3Sopenharmony_ci} 17753a5a1b3Sopenharmony_ci 17853a5a1b3Sopenharmony_cistatic void ensure_ports_started(struct userdata* u) { 17953a5a1b3Sopenharmony_ci unsigned i; 18053a5a1b3Sopenharmony_ci char *escaped; 18153a5a1b3Sopenharmony_ci pa_assert(u); 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci for (i = 0; i < JACK_SS_COUNT; i++) 18453a5a1b3Sopenharmony_ci if (u->mod_args[i].enabled && !u->jack_module_index[i]) { 18553a5a1b3Sopenharmony_ci pa_strbuf *args_buf = pa_strbuf_new(); 18653a5a1b3Sopenharmony_ci char *args; 18753a5a1b3Sopenharmony_ci pa_module *m; 18853a5a1b3Sopenharmony_ci pa_strbuf_printf(args_buf, "connect=%s", pa_yes_no(u->autoconnect_ports)); 18953a5a1b3Sopenharmony_ci if (u->mod_args[i].name) { 19053a5a1b3Sopenharmony_ci escaped = pa_escape(u->mod_args[i].name, "'"); 19153a5a1b3Sopenharmony_ci pa_strbuf_printf(args_buf, " %s_name='%s'", modtypes[i], escaped); 19253a5a1b3Sopenharmony_ci pa_xfree(escaped); 19353a5a1b3Sopenharmony_ci } 19453a5a1b3Sopenharmony_ci if (!pa_proplist_isempty(u->mod_args[i].proplist)) { 19553a5a1b3Sopenharmony_ci escaped = proplist_to_arg(u->mod_args[i].proplist); 19653a5a1b3Sopenharmony_ci pa_strbuf_printf(args_buf, " %s_properties='%s'", modtypes[i], escaped); 19753a5a1b3Sopenharmony_ci pa_xfree(escaped); 19853a5a1b3Sopenharmony_ci } 19953a5a1b3Sopenharmony_ci if (u->mod_args[i].client_name) { 20053a5a1b3Sopenharmony_ci escaped = pa_escape(u->mod_args[i].client_name, "'"); 20153a5a1b3Sopenharmony_ci pa_strbuf_printf(args_buf, " client_name='%s'", escaped); 20253a5a1b3Sopenharmony_ci pa_xfree(escaped); 20353a5a1b3Sopenharmony_ci } 20453a5a1b3Sopenharmony_ci if (u->mod_args[i].channels > 0) 20553a5a1b3Sopenharmony_ci pa_strbuf_printf(args_buf, " channels=%" PRIu32, u->mod_args[i].channels); 20653a5a1b3Sopenharmony_ci if (u->mod_args[i].channel_map.channels > 0) { 20753a5a1b3Sopenharmony_ci char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; 20853a5a1b3Sopenharmony_ci pa_channel_map_snprint(cm, sizeof(cm), &u->mod_args[i].channel_map); 20953a5a1b3Sopenharmony_ci pa_strbuf_printf(args_buf, " channel_map='%s'", cm); 21053a5a1b3Sopenharmony_ci } 21153a5a1b3Sopenharmony_ci args = pa_strbuf_to_string_free(args_buf); 21253a5a1b3Sopenharmony_ci pa_module_load(&m, u->core, modnames[i], args); 21353a5a1b3Sopenharmony_ci pa_xfree(args); 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci if (m) { 21653a5a1b3Sopenharmony_ci pa_log_info("Successfully started %s.", modnames[i]); 21753a5a1b3Sopenharmony_ci u->jack_module_index[i] = m->index; 21853a5a1b3Sopenharmony_ci } 21953a5a1b3Sopenharmony_ci else 22053a5a1b3Sopenharmony_ci pa_log_info("Failed to start %s.", modnames[i]); 22153a5a1b3Sopenharmony_ci } 22253a5a1b3Sopenharmony_ci} 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_cistatic bool check_service_started(struct userdata* u) { 22553a5a1b3Sopenharmony_ci DBusError error; 22653a5a1b3Sopenharmony_ci DBusMessage *m = NULL, *reply = NULL; 22753a5a1b3Sopenharmony_ci bool new_status = false; 22853a5a1b3Sopenharmony_ci dbus_bool_t call_result; 22953a5a1b3Sopenharmony_ci pa_assert(u); 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_ci dbus_error_init(&error); 23253a5a1b3Sopenharmony_ci 23353a5a1b3Sopenharmony_ci /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */ 23453a5a1b3Sopenharmony_ci if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u->connection), 23553a5a1b3Sopenharmony_ci JACK_SERVICE_NAME, &error)) { 23653a5a1b3Sopenharmony_ci pa_log_debug("jackdbus isn't running."); 23753a5a1b3Sopenharmony_ci goto finish; 23853a5a1b3Sopenharmony_ci } 23953a5a1b3Sopenharmony_ci 24053a5a1b3Sopenharmony_ci if (!(m = dbus_message_new_method_call(JACK_SERVICE_NAME, JACK_INTERFACE_PATH, JACK_INTERFACE_NAME, "IsStarted"))) { 24153a5a1b3Sopenharmony_ci pa_log("Failed to allocate IsStarted() method call."); 24253a5a1b3Sopenharmony_ci goto finish; 24353a5a1b3Sopenharmony_ci } 24453a5a1b3Sopenharmony_ci 24553a5a1b3Sopenharmony_ci if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) { 24653a5a1b3Sopenharmony_ci pa_log("IsStarted() call failed: %s: %s", error.name, error.message); 24753a5a1b3Sopenharmony_ci goto finish; 24853a5a1b3Sopenharmony_ci } 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci if (!dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &call_result, DBUS_TYPE_INVALID)) { 25153a5a1b3Sopenharmony_ci pa_log("IsStarted() call return failed: %s: %s", error.name, error.message); 25253a5a1b3Sopenharmony_ci goto finish; 25353a5a1b3Sopenharmony_ci } 25453a5a1b3Sopenharmony_ci 25553a5a1b3Sopenharmony_ci new_status = call_result; 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_cifinish: 25853a5a1b3Sopenharmony_ci if (m) 25953a5a1b3Sopenharmony_ci dbus_message_unref(m); 26053a5a1b3Sopenharmony_ci if (reply) 26153a5a1b3Sopenharmony_ci dbus_message_unref(reply); 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci dbus_error_free(&error); 26453a5a1b3Sopenharmony_ci if (new_status) 26553a5a1b3Sopenharmony_ci ensure_ports_started(u); 26653a5a1b3Sopenharmony_ci else 26753a5a1b3Sopenharmony_ci ensure_ports_stopped(u); 26853a5a1b3Sopenharmony_ci u->is_service_started = new_status; 26953a5a1b3Sopenharmony_ci return new_status; 27053a5a1b3Sopenharmony_ci} 27153a5a1b3Sopenharmony_ci 27253a5a1b3Sopenharmony_cistatic DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) { 27353a5a1b3Sopenharmony_ci struct userdata *u = NULL; 27453a5a1b3Sopenharmony_ci DBusError error; 27553a5a1b3Sopenharmony_ci 27653a5a1b3Sopenharmony_ci pa_assert(userdata); 27753a5a1b3Sopenharmony_ci u = ((pa_module*) userdata)->userdata; 27853a5a1b3Sopenharmony_ci pa_assert(u); 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci dbus_error_init(&error); 28153a5a1b3Sopenharmony_ci 28253a5a1b3Sopenharmony_ci if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { 28353a5a1b3Sopenharmony_ci const char *name, *old, *new; 28453a5a1b3Sopenharmony_ci if (!dbus_message_get_args(s, &error, 28553a5a1b3Sopenharmony_ci DBUS_TYPE_STRING, &name, 28653a5a1b3Sopenharmony_ci DBUS_TYPE_STRING, &old, 28753a5a1b3Sopenharmony_ci DBUS_TYPE_STRING, &new, 28853a5a1b3Sopenharmony_ci DBUS_TYPE_INVALID)) 28953a5a1b3Sopenharmony_ci goto finish; 29053a5a1b3Sopenharmony_ci if (!pa_streq(name, JACK_SERVICE_NAME)) 29153a5a1b3Sopenharmony_ci goto finish; 29253a5a1b3Sopenharmony_ci 29353a5a1b3Sopenharmony_ci ensure_ports_stopped(u); 29453a5a1b3Sopenharmony_ci check_service_started(u); 29553a5a1b3Sopenharmony_ci } 29653a5a1b3Sopenharmony_ci 29753a5a1b3Sopenharmony_ci else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStarted")) { 29853a5a1b3Sopenharmony_ci ensure_ports_stopped(u); 29953a5a1b3Sopenharmony_ci check_service_started(u); 30053a5a1b3Sopenharmony_ci } 30153a5a1b3Sopenharmony_ci 30253a5a1b3Sopenharmony_ci else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStopped")) { 30353a5a1b3Sopenharmony_ci ensure_ports_stopped(u); 30453a5a1b3Sopenharmony_ci } 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_cifinish: 30753a5a1b3Sopenharmony_ci dbus_error_free(&error); 30853a5a1b3Sopenharmony_ci return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 30953a5a1b3Sopenharmony_ci} 31053a5a1b3Sopenharmony_ci 31153a5a1b3Sopenharmony_ciint pa__init(pa_module *m) { 31253a5a1b3Sopenharmony_ci DBusError error; 31353a5a1b3Sopenharmony_ci pa_dbus_connection *connection = NULL; 31453a5a1b3Sopenharmony_ci struct userdata *u = NULL; 31553a5a1b3Sopenharmony_ci pa_modargs *ma; 31653a5a1b3Sopenharmony_ci uint32_t channels = 0; 31753a5a1b3Sopenharmony_ci unsigned i; 31853a5a1b3Sopenharmony_ci char argname[32]; 31953a5a1b3Sopenharmony_ci const char *name; 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci pa_assert(m); 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci dbus_error_init(&error); 32453a5a1b3Sopenharmony_ci 32553a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 32653a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments"); 32753a5a1b3Sopenharmony_ci goto fail; 32853a5a1b3Sopenharmony_ci } 32953a5a1b3Sopenharmony_ci 33053a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 33153a5a1b3Sopenharmony_ci u->core = m->core; 33253a5a1b3Sopenharmony_ci u->module = m; 33353a5a1b3Sopenharmony_ci u->autoconnect_ports = true; 33453a5a1b3Sopenharmony_ci 33553a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "connect", &u->autoconnect_ports) < 0) { 33653a5a1b3Sopenharmony_ci pa_log("Failed to parse connect= argument."); 33753a5a1b3Sopenharmony_ci goto fail; 33853a5a1b3Sopenharmony_ci } 33953a5a1b3Sopenharmony_ci 34053a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || (channels > 0 && !pa_channels_valid(channels))) { 34153a5a1b3Sopenharmony_ci pa_log("Failed to parse channels= argument."); 34253a5a1b3Sopenharmony_ci goto fail; 34353a5a1b3Sopenharmony_ci } 34453a5a1b3Sopenharmony_ci 34553a5a1b3Sopenharmony_ci for (i = 0; i < JACK_SS_COUNT; i++) { 34653a5a1b3Sopenharmony_ci u->mod_args[i].enabled = true; 34753a5a1b3Sopenharmony_ci pa_snprintf(argname, sizeof(argname), "%s_enabled", modtypes[i]); 34853a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, argname, &u->mod_args[i].enabled) < 0) { 34953a5a1b3Sopenharmony_ci pa_log("Failed to parse %s= argument.", argname); 35053a5a1b3Sopenharmony_ci goto fail; 35153a5a1b3Sopenharmony_ci } 35253a5a1b3Sopenharmony_ci 35353a5a1b3Sopenharmony_ci pa_snprintf(argname, sizeof(argname), "%s_name", modtypes[i]); 35453a5a1b3Sopenharmony_ci name = pa_modargs_get_value(ma, argname, NULL); 35553a5a1b3Sopenharmony_ci u->mod_args[i].name = pa_xstrdup(name); 35653a5a1b3Sopenharmony_ci 35753a5a1b3Sopenharmony_ci u->mod_args[i].proplist = pa_proplist_new(); 35853a5a1b3Sopenharmony_ci pa_snprintf(argname, sizeof(argname), "%s_properties", modtypes[i]); 35953a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, argname, u->mod_args[i].proplist, PA_UPDATE_REPLACE) < 0) { 36053a5a1b3Sopenharmony_ci pa_log("Invalid %s properties", modtypes[i]); 36153a5a1b3Sopenharmony_ci goto fail; 36253a5a1b3Sopenharmony_ci } 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_ci pa_snprintf(argname, sizeof(argname), "%s_client_name", modtypes[i]); 36553a5a1b3Sopenharmony_ci name = pa_modargs_get_value(ma, argname, NULL); 36653a5a1b3Sopenharmony_ci u->mod_args[i].client_name = pa_xstrdup(name); 36753a5a1b3Sopenharmony_ci 36853a5a1b3Sopenharmony_ci u->mod_args[i].channels = channels; 36953a5a1b3Sopenharmony_ci pa_snprintf(argname, sizeof(argname), "%s_channels", modtypes[i]); 37053a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, argname, &u->mod_args[i].channels) < 0 37153a5a1b3Sopenharmony_ci || (u->mod_args[i].channels > 0 && !pa_channels_valid(u->mod_args[i].channels))) { 37253a5a1b3Sopenharmony_ci pa_log("Failed to parse %s= argument.", argname); 37353a5a1b3Sopenharmony_ci goto fail; 37453a5a1b3Sopenharmony_ci } 37553a5a1b3Sopenharmony_ci 37653a5a1b3Sopenharmony_ci pa_channel_map_init(&u->mod_args[i].channel_map); 37753a5a1b3Sopenharmony_ci pa_snprintf(argname, sizeof(argname), "%s_channel_map", modtypes[i]); 37853a5a1b3Sopenharmony_ci if (pa_modargs_get_value(ma, argname, NULL)) { 37953a5a1b3Sopenharmony_ci if (pa_modargs_get_channel_map(ma, argname, &u->mod_args[i].channel_map) < 0 38053a5a1b3Sopenharmony_ci || (u->mod_args[i].channels > 0 && u->mod_args[i].channel_map.channels != u->mod_args[i].channels)) { 38153a5a1b3Sopenharmony_ci pa_log("Failed to parse %s= argument.", argname); 38253a5a1b3Sopenharmony_ci goto fail; 38353a5a1b3Sopenharmony_ci } 38453a5a1b3Sopenharmony_ci } 38553a5a1b3Sopenharmony_ci } 38653a5a1b3Sopenharmony_ci 38753a5a1b3Sopenharmony_ci if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { 38853a5a1b3Sopenharmony_ci 38953a5a1b3Sopenharmony_ci if (connection) 39053a5a1b3Sopenharmony_ci pa_dbus_connection_unref(connection); 39153a5a1b3Sopenharmony_ci 39253a5a1b3Sopenharmony_ci pa_log_error("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); 39353a5a1b3Sopenharmony_ci goto fail; 39453a5a1b3Sopenharmony_ci } 39553a5a1b3Sopenharmony_ci u->connection = connection; 39653a5a1b3Sopenharmony_ci 39753a5a1b3Sopenharmony_ci if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), dbus_filter_handler, m, NULL)) { 39853a5a1b3Sopenharmony_ci pa_log_error("Unable to add D-Bus filter"); 39953a5a1b3Sopenharmony_ci goto fail; 40053a5a1b3Sopenharmony_ci } 40153a5a1b3Sopenharmony_ci u->filter_added = 1; 40253a5a1b3Sopenharmony_ci 40353a5a1b3Sopenharmony_ci if (pa_dbus_add_matches( 40453a5a1b3Sopenharmony_ci pa_dbus_connection_get(connection), &error, SERVICE_FILTER, 40553a5a1b3Sopenharmony_ci RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL) < 0) { 40653a5a1b3Sopenharmony_ci pa_log_error("Unable to subscribe to signals: %s: %s", error.name, error.message); 40753a5a1b3Sopenharmony_ci goto fail; 40853a5a1b3Sopenharmony_ci } 40953a5a1b3Sopenharmony_ci u->match_added = 1; 41053a5a1b3Sopenharmony_ci 41153a5a1b3Sopenharmony_ci check_service_started(u); 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 41453a5a1b3Sopenharmony_ci return 0; 41553a5a1b3Sopenharmony_ci 41653a5a1b3Sopenharmony_cifail: 41753a5a1b3Sopenharmony_ci if (ma) 41853a5a1b3Sopenharmony_ci pa_modargs_free(ma); 41953a5a1b3Sopenharmony_ci 42053a5a1b3Sopenharmony_ci dbus_error_free(&error); 42153a5a1b3Sopenharmony_ci pa__done(m); 42253a5a1b3Sopenharmony_ci 42353a5a1b3Sopenharmony_ci return -1; 42453a5a1b3Sopenharmony_ci} 42553a5a1b3Sopenharmony_ci 42653a5a1b3Sopenharmony_civoid pa__done(pa_module *m) { 42753a5a1b3Sopenharmony_ci struct userdata *u; 42853a5a1b3Sopenharmony_ci unsigned i; 42953a5a1b3Sopenharmony_ci 43053a5a1b3Sopenharmony_ci pa_assert(m); 43153a5a1b3Sopenharmony_ci 43253a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 43353a5a1b3Sopenharmony_ci return; 43453a5a1b3Sopenharmony_ci 43553a5a1b3Sopenharmony_ci ensure_ports_stopped(u); 43653a5a1b3Sopenharmony_ci 43753a5a1b3Sopenharmony_ci if (u->match_added) { 43853a5a1b3Sopenharmony_ci pa_dbus_remove_matches( 43953a5a1b3Sopenharmony_ci pa_dbus_connection_get(u->connection), SERVICE_FILTER, 44053a5a1b3Sopenharmony_ci RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL); 44153a5a1b3Sopenharmony_ci } 44253a5a1b3Sopenharmony_ci 44353a5a1b3Sopenharmony_ci if (u->filter_added) { 44453a5a1b3Sopenharmony_ci dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), dbus_filter_handler, m); 44553a5a1b3Sopenharmony_ci } 44653a5a1b3Sopenharmony_ci 44753a5a1b3Sopenharmony_ci if (u->connection) { 44853a5a1b3Sopenharmony_ci pa_dbus_connection_unref(u->connection); 44953a5a1b3Sopenharmony_ci } 45053a5a1b3Sopenharmony_ci 45153a5a1b3Sopenharmony_ci for (i = 0; i < JACK_SS_COUNT; i++) { 45253a5a1b3Sopenharmony_ci pa_xfree(u->mod_args[i].name); 45353a5a1b3Sopenharmony_ci 45453a5a1b3Sopenharmony_ci if (u->mod_args[i].proplist) 45553a5a1b3Sopenharmony_ci pa_proplist_free(u->mod_args[i].proplist); 45653a5a1b3Sopenharmony_ci 45753a5a1b3Sopenharmony_ci pa_xfree(u->mod_args[i].client_name); 45853a5a1b3Sopenharmony_ci } 45953a5a1b3Sopenharmony_ci 46053a5a1b3Sopenharmony_ci pa_xfree(u); 46153a5a1b3Sopenharmony_ci} 462