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
);
43 PA_MODULE_USAGE("connect=<connect ports?>");
45 #define JACK_SERVICE_NAME "org.jackaudio.service"
46 #define JACK_INTERFACE_NAME "org.jackaudio.JackControl"
47 #define JACK_INTERFACE_PATH "/org/jackaudio/Controller"
49 #define SERVICE_FILTER \
51 "sender='" DBUS_SERVICE_DBUS "'," \
52 "interface='" DBUS_INTERFACE_DBUS "'," \
53 "member='NameOwnerChanged'," \
54 "arg0='" JACK_SERVICE_NAME "'"
56 #define RUNNING_FILTER(_a) \
58 "sender='" JACK_SERVICE_NAME "'," \
59 "interface='" JACK_INTERFACE_NAME "'," \
62 static const char* const valid_modargs
[] = {
67 #define JACK_SS_SINK 0
68 #define JACK_SS_SOURCE 1
69 #define JACK_SS_COUNT 2
71 static const char* const modnames
[JACK_SS_COUNT
] = {
80 pa_dbus_connection
*connection
;
81 pa_bool_t filter_added
, match_added
;
82 pa_bool_t is_service_started
;
83 pa_bool_t autoconnect_ports
;
84 /* Using index here protects us from module unloading without us knowing */
85 int jack_module_index
[JACK_SS_COUNT
];
89 static void ensure_ports_stopped(struct userdata
* u
) {
93 for (i
= 0; i
< JACK_SS_COUNT
; i
++)
94 if (u
->jack_module_index
[i
]) {
95 pa_module_unload_request_by_index(u
->core
, u
->jack_module_index
[i
], TRUE
);
96 u
->jack_module_index
[i
] = 0;
97 pa_log_info("Stopped %s.", modnames
[i
]);
101 static void ensure_ports_started(struct userdata
* u
) {
105 for (i
= 0; i
< JACK_SS_COUNT
; i
++)
106 if (!u
->jack_module_index
[i
]) {
109 args
= pa_sprintf_malloc("connect=%s", pa_yes_no(u
->autoconnect_ports
));
110 m
= pa_module_load(u
->core
, modnames
[i
], args
);
114 pa_log_info("Successfully started %s.", modnames
[i
]);
115 u
->jack_module_index
[i
] = m
->index
;
118 pa_log_info("Failed to start %s.", modnames
[i
]);
123 static pa_bool_t
check_service_started(struct userdata
* u
) {
125 DBusMessage
*m
= NULL
, *reply
= NULL
;
126 pa_bool_t new_status
= FALSE
;
127 dbus_bool_t call_result
;
130 dbus_error_init(&error
);
132 /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */
133 if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u
->connection
),
134 JACK_SERVICE_NAME
, &error
)) {
135 pa_log_debug("jackdbus isn't running.");
139 if (!(m
= dbus_message_new_method_call(JACK_SERVICE_NAME
, JACK_INTERFACE_PATH
, JACK_INTERFACE_NAME
, "IsStarted"))) {
140 pa_log("Failed to allocate IsStarted() method call.");
144 if (!(reply
= dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u
->connection
), m
, -1, &error
))) {
145 pa_log("IsStarted() call failed: %s: %s", error
.name
, error
.message
);
149 if (!dbus_message_get_args(reply
, &error
, DBUS_TYPE_BOOLEAN
, &call_result
, DBUS_TYPE_INVALID
)) {
150 pa_log("IsStarted() call return failed: %s: %s", error
.name
, error
.message
);
154 new_status
= call_result
;
158 dbus_message_unref(m
);
160 dbus_message_unref(reply
);
162 dbus_error_free(&error
);
164 ensure_ports_started(u
);
166 ensure_ports_stopped(u
);
167 u
->is_service_started
= new_status
;
171 static DBusHandlerResult
dbus_filter_handler(DBusConnection
*c
, DBusMessage
*s
, void *userdata
) {
172 struct userdata
*u
= NULL
;
176 u
= ((pa_module
*) userdata
)->userdata
;
179 dbus_error_init(&error
);
181 if (dbus_message_is_signal(s
, DBUS_INTERFACE_DBUS
, "NameOwnerChanged")) {
182 const char *name
, *old
, *new;
183 if (!dbus_message_get_args(s
, &error
,
184 DBUS_TYPE_STRING
, &name
,
185 DBUS_TYPE_STRING
, &old
,
186 DBUS_TYPE_STRING
, &new,
189 if (strcmp(name
, JACK_SERVICE_NAME
))
192 ensure_ports_stopped(u
);
193 check_service_started(u
);
196 else if (dbus_message_is_signal(s
, JACK_INTERFACE_NAME
, "ServerStarted")) {
197 ensure_ports_stopped(u
);
198 check_service_started(u
);
201 else if (dbus_message_is_signal(s
, JACK_INTERFACE_NAME
, "ServerStopped")) {
202 ensure_ports_stopped(u
);
206 dbus_error_free(&error
);
207 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
210 int pa__init(pa_module
*m
) {
212 pa_dbus_connection
*connection
= NULL
;
213 struct userdata
*u
= NULL
;
218 dbus_error_init(&error
);
220 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
221 pa_log("Failed to parse module arguments");
225 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
228 u
->autoconnect_ports
= TRUE
;
230 if (pa_modargs_get_value_boolean(ma
, "connect", &u
->autoconnect_ports
) < 0) {
231 pa_log("Failed to parse connect= argument.");
235 if (!(connection
= pa_dbus_bus_get(m
->core
, DBUS_BUS_SESSION
, &error
)) || dbus_error_is_set(&error
)) {
238 pa_dbus_connection_unref(connection
);
240 pa_log_error("Unable to contact D-Bus session bus: %s: %s", error
.name
, error
.message
);
243 u
->connection
= connection
;
245 if (!dbus_connection_add_filter(pa_dbus_connection_get(connection
), dbus_filter_handler
, m
, NULL
)) {
246 pa_log_error("Unable to add D-Bus filter");
251 if (pa_dbus_add_matches(
252 pa_dbus_connection_get(connection
), &error
, SERVICE_FILTER
,
253 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL
) < 0) {
254 pa_log_error("Unable to subscribe to signals: %s: %s", error
.name
, error
.message
);
259 check_service_started(u
);
267 dbus_error_free(&error
);
273 void pa__done(pa_module
*m
) {
278 if (!(u
= m
->userdata
))
281 ensure_ports_stopped(u
);
283 if (u
->match_added
) {
284 pa_dbus_remove_matches(
285 pa_dbus_connection_get(u
->connection
), SERVICE_FILTER
,
286 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL
);
289 if (u
->filter_added
) {
290 dbus_connection_remove_filter(pa_dbus_connection_get(u
->connection
), dbus_filter_handler
, m
);
294 pa_dbus_connection_unref(u
->connection
);