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
] = {
82 pa_dbus_connection
*connection
;
83 pa_bool_t filter_added
, match_added
;
84 pa_bool_t is_service_started
;
85 pa_bool_t autoconnect_ports
;
87 /* Using index here protects us from module unloading without us knowing */
88 int jack_module_index
[JACK_SS_COUNT
];
91 static void ensure_ports_stopped(struct userdata
* u
) {
95 for (i
= 0; i
< JACK_SS_COUNT
; i
++)
96 if (u
->jack_module_index
[i
]) {
97 pa_module_unload_request_by_index(u
->core
, u
->jack_module_index
[i
], TRUE
);
98 u
->jack_module_index
[i
] = 0;
99 pa_log_info("Stopped %s.", modnames
[i
]);
103 static void ensure_ports_started(struct userdata
* u
) {
107 for (i
= 0; i
< JACK_SS_COUNT
; i
++)
108 if (!u
->jack_module_index
[i
]) {
111 if (u
->channels
> 0) {
112 args
= pa_sprintf_malloc("connect=%s channels=%" PRIu32
, pa_yes_no(u
->autoconnect_ports
), u
->channels
);
114 args
= pa_sprintf_malloc("connect=%s", pa_yes_no(u
->autoconnect_ports
));
116 m
= pa_module_load(u
->core
, modnames
[i
], args
);
120 pa_log_info("Successfully started %s.", modnames
[i
]);
121 u
->jack_module_index
[i
] = m
->index
;
124 pa_log_info("Failed to start %s.", modnames
[i
]);
128 static pa_bool_t
check_service_started(struct userdata
* u
) {
130 DBusMessage
*m
= NULL
, *reply
= NULL
;
131 pa_bool_t new_status
= FALSE
;
132 dbus_bool_t call_result
;
135 dbus_error_init(&error
);
137 /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */
138 if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u
->connection
),
139 JACK_SERVICE_NAME
, &error
)) {
140 pa_log_debug("jackdbus isn't running.");
144 if (!(m
= dbus_message_new_method_call(JACK_SERVICE_NAME
, JACK_INTERFACE_PATH
, JACK_INTERFACE_NAME
, "IsStarted"))) {
145 pa_log("Failed to allocate IsStarted() method call.");
149 if (!(reply
= dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u
->connection
), m
, -1, &error
))) {
150 pa_log("IsStarted() call failed: %s: %s", error
.name
, error
.message
);
154 if (!dbus_message_get_args(reply
, &error
, DBUS_TYPE_BOOLEAN
, &call_result
, DBUS_TYPE_INVALID
)) {
155 pa_log("IsStarted() call return failed: %s: %s", error
.name
, error
.message
);
159 new_status
= call_result
;
163 dbus_message_unref(m
);
165 dbus_message_unref(reply
);
167 dbus_error_free(&error
);
169 ensure_ports_started(u
);
171 ensure_ports_stopped(u
);
172 u
->is_service_started
= new_status
;
176 static DBusHandlerResult
dbus_filter_handler(DBusConnection
*c
, DBusMessage
*s
, void *userdata
) {
177 struct userdata
*u
= NULL
;
181 u
= ((pa_module
*) userdata
)->userdata
;
184 dbus_error_init(&error
);
186 if (dbus_message_is_signal(s
, DBUS_INTERFACE_DBUS
, "NameOwnerChanged")) {
187 const char *name
, *old
, *new;
188 if (!dbus_message_get_args(s
, &error
,
189 DBUS_TYPE_STRING
, &name
,
190 DBUS_TYPE_STRING
, &old
,
191 DBUS_TYPE_STRING
, &new,
194 if (!pa_streq(name
, JACK_SERVICE_NAME
))
197 ensure_ports_stopped(u
);
198 check_service_started(u
);
201 else if (dbus_message_is_signal(s
, JACK_INTERFACE_NAME
, "ServerStarted")) {
202 ensure_ports_stopped(u
);
203 check_service_started(u
);
206 else if (dbus_message_is_signal(s
, JACK_INTERFACE_NAME
, "ServerStopped")) {
207 ensure_ports_stopped(u
);
211 dbus_error_free(&error
);
212 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
215 int pa__init(pa_module
*m
) {
217 pa_dbus_connection
*connection
= NULL
;
218 struct userdata
*u
= NULL
;
223 dbus_error_init(&error
);
225 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
226 pa_log("Failed to parse module arguments");
230 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
233 u
->autoconnect_ports
= TRUE
;
236 if (pa_modargs_get_value_boolean(ma
, "connect", &u
->autoconnect_ports
) < 0) {
237 pa_log("Failed to parse connect= argument.");
241 if (pa_modargs_get_value_u32(ma
, "channels", &u
->channels
) < 0 || u
->channels
> PA_CHANNELS_MAX
) {
242 pa_log("Failed to parse channels= argument.");
246 if (!(connection
= pa_dbus_bus_get(m
->core
, DBUS_BUS_SESSION
, &error
)) || dbus_error_is_set(&error
)) {
249 pa_dbus_connection_unref(connection
);
251 pa_log_error("Unable to contact D-Bus session bus: %s: %s", error
.name
, error
.message
);
254 u
->connection
= connection
;
256 if (!dbus_connection_add_filter(pa_dbus_connection_get(connection
), dbus_filter_handler
, m
, NULL
)) {
257 pa_log_error("Unable to add D-Bus filter");
262 if (pa_dbus_add_matches(
263 pa_dbus_connection_get(connection
), &error
, SERVICE_FILTER
,
264 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL
) < 0) {
265 pa_log_error("Unable to subscribe to signals: %s: %s", error
.name
, error
.message
);
270 check_service_started(u
);
279 dbus_error_free(&error
);
285 void pa__done(pa_module
*m
) {
290 if (!(u
= m
->userdata
))
293 ensure_ports_stopped(u
);
295 if (u
->match_added
) {
296 pa_dbus_remove_matches(
297 pa_dbus_connection_get(u
->connection
), SERVICE_FILTER
,
298 RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL
);
301 if (u
->filter_added
) {
302 dbus_connection_remove_filter(pa_dbus_connection_get(u
->connection
), dbus_filter_handler
, m
);
306 pa_dbus_connection_unref(u
->connection
);