2 This file is part of PulseAudio.
4 Written by David Henningsson <david.henningsson@canonical.com>
5 Copyright 2010 Canonical Ltd.
7 Some code taken from other parts of PulseAudio, these are
8 Copyright 2006-2009 Lennart Poettering
10 PulseAudio is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published
12 by the Free Software Foundation; either version 2.1 of the License,
13 or (at your option) any later version.
15 PulseAudio is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with PulseAudio; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <pulse/xmalloc.h>
32 #include <pulsecore/log.h>
33 #include <pulsecore/modargs.h>
34 #include <pulsecore/core-util.h>
35 #include <pulsecore/dbus-shared.h>
37 #include "module-jackdbus-detect-symdef.h"
39 PA_MODULE_AUTHOR("David Henningsson");
40 PA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started");
41 PA_MODULE_LOAD_ONCE(TRUE
);
42 PA_MODULE_VERSION(PACKAGE_VERSION
);
44 "channels=<number of channels> "
45 "connect=<connect ports?>");
47 #define JACK_SERVICE_NAME "org.jackaudio.service"
48 #define JACK_INTERFACE_NAME "org.jackaudio.JackControl"
49 #define JACK_INTERFACE_PATH "/org/jackaudio/Controller"
51 #define SERVICE_FILTER \
53 "sender='" DBUS_SERVICE_DBUS "'," \
54 "interface='" DBUS_INTERFACE_DBUS "'," \
55 "member='NameOwnerChanged'," \
56 "arg0='" JACK_SERVICE_NAME "'"
58 #define RUNNING_FILTER(_a) \
60 "sender='" JACK_SERVICE_NAME "'," \
61 "interface='" JACK_INTERFACE_NAME "'," \
64 static const char* const valid_modargs
[] = {
70 #define JACK_SS_SINK 0
71 #define JACK_SS_SOURCE 1
72 #define JACK_SS_COUNT 2
74 static const char* const modnames
[JACK_SS_COUNT
] = {
83 pa_dbus_connection
*connection
;
84 pa_bool_t filter_added
, match_added
;
85 pa_bool_t is_service_started
;
86 pa_bool_t autoconnect_ports
;
88 /* Using index here protects us from module unloading without us knowing */
89 int jack_module_index
[JACK_SS_COUNT
];
93 static void ensure_ports_stopped(struct userdata
* u
) {
97 for (i
= 0; i
< JACK_SS_COUNT
; i
++)
98 if (u
->jack_module_index
[i
]) {
99 pa_module_unload_request_by_index(u
->core
, u
->jack_module_index
[i
], TRUE
);
100 u
->jack_module_index
[i
] = 0;
101 pa_log_info("Stopped %s.", modnames
[i
]);
105 static void ensure_ports_started(struct userdata
* u
) {
109 for (i
= 0; i
< JACK_SS_COUNT
; i
++)
110 if (!u
->jack_module_index
[i
]) {
113 if (u
->channels
> 0) {
114 args
= pa_sprintf_malloc("connect=%s channels=%" PRIu32
, pa_yes_no(u
->autoconnect_ports
), u
->channels
);
116 args
= pa_sprintf_malloc("connect=%s", pa_yes_no(u
->autoconnect_ports
));
118 m
= pa_module_load(u
->core
, modnames
[i
], args
);
122 pa_log_info("Successfully started %s.", modnames
[i
]);
123 u
->jack_module_index
[i
] = m
->index
;
126 pa_log_info("Failed to start %s.", modnames
[i
]);
131 static pa_bool_t
check_service_started(struct userdata
* u
) {
133 DBusMessage
*m
= NULL
, *reply
= NULL
;
134 pa_bool_t new_status
= FALSE
;
135 dbus_bool_t call_result
;
138 dbus_error_init(&error
);
140 /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */
141 if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u
->connection
),
142 JACK_SERVICE_NAME
, &error
)) {
143 pa_log_debug("jackdbus isn't running.");
147 if (!(m
= dbus_message_new_method_call(JACK_SERVICE_NAME
, JACK_INTERFACE_PATH
, JACK_INTERFACE_NAME
, "IsStarted"))) {
148 pa_log("Failed to allocate IsStarted() method call.");
152 if (!(reply
= dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u
->connection
), m
, -1, &error
))) {
153 pa_log("IsStarted() call failed: %s: %s", error
.name
, error
.message
);
157 if (!dbus_message_get_args(reply
, &error
, DBUS_TYPE_BOOLEAN
, &call_result
, DBUS_TYPE_INVALID
)) {
158 pa_log("IsStarted() call return failed: %s: %s", error
.name
, error
.message
);
162 new_status
= call_result
;
166 dbus_message_unref(m
);
168 dbus_message_unref(reply
);
170 dbus_error_free(&error
);
172 ensure_ports_started(u
);
174 ensure_ports_stopped(u
);
175 u
->is_service_started
= new_status
;
179 static DBusHandlerResult
dbus_filter_handler(DBusConnection
*c
, DBusMessage
*s
, void *userdata
) {
180 struct userdata
*u
= NULL
;
184 u
= ((pa_module
*) userdata
)->userdata
;
187 dbus_error_init(&error
);
189 if (dbus_message_is_signal(s
, DBUS_INTERFACE_DBUS
, "NameOwnerChanged")) {
190 const char *name
, *old
, *new;
191 if (!dbus_message_get_args(s
, &error
,
192 DBUS_TYPE_STRING
, &name
,
193 DBUS_TYPE_STRING
, &old
,
194 DBUS_TYPE_STRING
, &new,
197 if (!pa_streq(name
, JACK_SERVICE_NAME
))
200 ensure_ports_stopped(u
);
201 check_service_started(u
);
204 else if (dbus_message_is_signal(s
, JACK_INTERFACE_NAME
, "ServerStarted")) {
205 ensure_ports_stopped(u
);
206 check_service_started(u
);
209 else if (dbus_message_is_signal(s
, JACK_INTERFACE_NAME
, "ServerStopped")) {
210 ensure_ports_stopped(u
);
214 dbus_error_free(&error
);
215 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
218 int pa__init(pa_module
*m
) {
220 pa_dbus_connection
*connection
= NULL
;
221 struct userdata
*u
= NULL
;
226 dbus_error_init(&error
);
228 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
229 pa_log("Failed to parse module arguments");
233 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
236 u
->autoconnect_ports
= TRUE
;
239 if (pa_modargs_get_value_boolean(ma
, "connect", &u
->autoconnect_ports
) < 0) {
240 pa_log("Failed to parse connect= argument.");
244 if (pa_modargs_get_value_u32(ma
, "channels", &u
->channels
) < 0 || u
->channels
> PA_CHANNELS_MAX
) {
245 pa_log("Failed to parse channels= argument.");
249 if (!(connection
= pa_dbus_bus_get(m
->core
, DBUS_BUS_SESSION
, &error
)) || dbus_error_is_set(&error
)) {
252 pa_dbus_connection_unref(connection
);
254 pa_log_error("Unable to contact D-Bus session bus: %s: %s", error
.name
, error
.message
);
257 u
->connection
= connection
;
259 if (!dbus_connection_add_filter(pa_dbus_connection_get(connection
), dbus_filter_handler
, m
, NULL
)) {
260 pa_log_error("Unable to add D-Bus filter");
265 if (pa_dbus_add_matches(
266 pa_dbus_connection_get(connection
), &error
, SERVICE_FILTER
,
267 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL
) < 0) {
268 pa_log_error("Unable to subscribe to signals: %s: %s", error
.name
, error
.message
);
273 check_service_started(u
);
282 dbus_error_free(&error
);
288 void pa__done(pa_module
*m
) {
293 if (!(u
= m
->userdata
))
296 ensure_ports_stopped(u
);
298 if (u
->match_added
) {
299 pa_dbus_remove_matches(
300 pa_dbus_connection_get(u
->connection
), SERVICE_FILTER
,
301 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL
);
304 if (u
->filter_added
) {
305 dbus_connection_remove_filter(pa_dbus_connection_get(u
->connection
), dbus_filter_handler
, m
);
309 pa_dbus_connection_unref(u
->connection
);