add dbus command to allow checking of wether hardware is currently exported
[a2jmidid.git] / dbus_iface_control.c
blobbd894e7078094767d624783a61a23e00dd49e978
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * ALSA SEQ < - > JACK MIDI bridge
5 * Copyright (c) 2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <stdbool.h>
22 #include <dbus/dbus.h>
23 #include <alsa/asoundlib.h>
24 #include <jack/jack.h>
25 #include <jack/midiport.h>
26 #include <jack/ringbuffer.h>
28 #include "dbus_internal.h"
29 #include "a2jmidid.h"
30 #include "log.h"
31 #include "list.h"
32 #include "structs.h"
33 #include "port_thread.h"
34 #include "conf.h"
36 #define INTERFACE_NAME "org.gna.home.a2jmidid.control"
38 void
39 a2j_dbus_signal_emit_bridge_started()
41 a2j_dbus_signal("/", INTERFACE_NAME, "bridge_started", DBUS_TYPE_INVALID);
44 void
45 a2j_dbus_signal_emit_bridge_stopped()
47 a2j_dbus_signal("/", INTERFACE_NAME, "bridge_stopped", DBUS_TYPE_INVALID);
50 static
51 void
52 a2j_dbus_exit(
53 struct a2j_dbus_method_call * call_ptr)
55 g_keep_walking = false;
56 a2j_dbus_construct_method_return_void(call_ptr);
59 static void a2j_dbus_set_hw_export(struct a2j_dbus_method_call * call_ptr)
61 DBusError error;
62 dbus_bool_t hw_export;
64 if (a2j_is_started())
66 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_BRIDGE_RUNNING, "Bridge is started");
67 return;
70 dbus_error_init(&error);
72 if (!dbus_message_get_args(
73 call_ptr->message,
74 &error,
75 DBUS_TYPE_BOOLEAN, &hw_export,
76 DBUS_TYPE_INVALID))
78 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\"", call_ptr->method_name);
79 dbus_error_free(&error);
80 return;
83 g_a2j_export_hw_ports = hw_export;
85 a2j_info("Hardware ports %s be exported.", g_a2j_export_hw_ports ? "will": "will not");
87 a2j_dbus_construct_method_return_void(call_ptr);
90 static void a2j_dbus_get_hw_export(struct a2j_dbus_method_call * call_ptr)
92 dbus_bool_t hw_export;
94 hw_export = g_a2j_export_hw_ports;
96 a2j_dbus_construct_method_return_single(
97 call_ptr,
98 DBUS_TYPE_BOOLEAN,
99 &hw_export);
102 static
103 void
104 a2j_dbus_start(
105 struct a2j_dbus_method_call * call_ptr)
107 if (!a2j_start())
109 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_GENERIC, "a2j_start() failed.");
111 else
113 a2j_dbus_construct_method_return_void(call_ptr);
117 static
118 void
119 a2j_dbus_stop(
120 struct a2j_dbus_method_call * call_ptr)
122 if (!a2j_stop())
124 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_GENERIC, "a2j_stop() failed.");
126 else
128 a2j_dbus_construct_method_return_void(call_ptr);
132 static
133 void
134 a2j_dbus_is_started(
135 struct a2j_dbus_method_call * call_ptr)
137 dbus_bool_t is_started;
139 is_started = a2j_is_started();
141 a2j_dbus_construct_method_return_single(
142 call_ptr,
143 DBUS_TYPE_BOOLEAN,
144 &is_started);
147 static
148 void
149 a2j_dbus_get_jack_client_name(
150 struct a2j_dbus_method_call * call_ptr)
152 const char * jack_client_name;
154 jack_client_name = A2J_JACK_CLIENT_NAME;
156 a2j_dbus_construct_method_return_single(
157 call_ptr,
158 DBUS_TYPE_STRING,
159 &jack_client_name);
162 static
163 void
164 a2j_dbus_map_alsa_to_jack_port(
165 struct a2j_dbus_method_call * call_ptr)
167 DBusError error;
168 dbus_uint32_t client_id;
169 dbus_uint32_t port_id;
170 dbus_bool_t map_playback;
171 snd_seq_addr_t addr;
172 struct a2j_port * port_ptr;
173 const char * direction_string;
174 struct a2j_stream * stream_ptr;
175 const char * jack_port;
177 dbus_error_init(&error);
179 if (!dbus_message_get_args(
180 call_ptr->message,
181 &error,
182 DBUS_TYPE_UINT32, &client_id,
183 DBUS_TYPE_UINT32, &port_id,
184 DBUS_TYPE_BOOLEAN, &map_playback,
185 DBUS_TYPE_INVALID))
187 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\"", call_ptr->method_name);
188 dbus_error_free(&error);
189 return;
192 if (!a2j_is_started())
194 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_BRIDGE_NOT_RUNNING, "Bridge not started");
195 return;
198 addr.client = client_id;
199 addr.port = port_id;
201 if (map_playback)
203 stream_ptr = g_a2j->stream + A2J_PORT_PLAYBACK;
204 direction_string = "playback";
206 else
208 stream_ptr = g_a2j->stream + A2J_PORT_CAPTURE;
209 direction_string = "capture";
212 port_ptr = a2j_find_port_by_addr(stream_ptr, addr);
213 if (port_ptr == NULL)
215 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_UNKNOWN_PORT, "Unknown ALSA sequencer port %u:%u (%s)", (unsigned int)client_id, (unsigned int)port_id, direction_string);
216 return;
219 jack_port = port_ptr->name;
221 a2j_info("map %u:%u (%s) -> '%s'", (unsigned int)client_id, (unsigned int)port_id, direction_string, jack_port);
223 a2j_dbus_construct_method_return_single(
224 call_ptr,
225 DBUS_TYPE_STRING,
226 &jack_port);
229 static
230 void
231 a2j_dbus_map_jack_port_to_alsa(
232 struct a2j_dbus_method_call * call_ptr)
234 snd_seq_client_info_t * client_info_ptr;
235 snd_seq_port_info_t * port_info_ptr;
236 DBusError error;
237 const char * jack_port;
238 struct a2j_port * port_ptr;
239 const char * client_name;
240 const char * port_name;
241 dbus_uint32_t client_id;
242 dbus_uint32_t port_id;
243 DBusMessageIter iter;
246 snd_seq_client_info_alloca(&client_info_ptr);
247 snd_seq_port_info_alloca(&port_info_ptr);
249 dbus_error_init(&error);
251 if (!dbus_message_get_args(
252 call_ptr->message,
253 &error,
254 DBUS_TYPE_STRING, &jack_port,
255 DBUS_TYPE_INVALID))
257 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\"", call_ptr->method_name);
258 dbus_error_free(&error);
259 return;
262 if (!a2j_is_started())
264 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_BRIDGE_NOT_RUNNING, "Bridge not started");
265 return;
268 port_ptr = a2j_find_port_by_jack_port_name(g_a2j->stream + A2J_PORT_CAPTURE, jack_port);
269 if (port_ptr == NULL)
271 port_ptr = a2j_find_port_by_jack_port_name(g_a2j->stream + A2J_PORT_PLAYBACK, jack_port);
272 if (port_ptr == NULL)
274 a2j_dbus_error(call_ptr, A2J_DBUS_ERROR_UNKNOWN_PORT, "Unknown JACK port '%s'", jack_port);
275 return;
279 if (snd_seq_get_any_client_info(g_a2j->seq, port_ptr->remote.client, client_info_ptr) >= 0)
281 client_name = snd_seq_client_info_get_name(client_info_ptr);
283 else
285 client_name = "";
288 if (snd_seq_get_any_port_info(g_a2j->seq, port_ptr->remote.client, port_ptr->remote.port, port_info_ptr) >= 0)
290 port_name = snd_seq_port_info_get_name(port_info_ptr);
292 else
294 port_name = "";
297 a2j_info("map '%s' -> %u:%u ('%s':'%s')", jack_port, (unsigned int)port_ptr->remote.client, (unsigned int)port_ptr->remote.port, client_name, port_name);
299 client_id = port_ptr->remote.client;
300 port_id = port_ptr->remote.port;
302 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
303 if (call_ptr->reply == NULL)
305 goto fail;
308 dbus_message_iter_init_append(call_ptr->reply, &iter);
310 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &client_id))
312 goto fail_unref;
315 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &port_id))
317 goto fail_unref;
320 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &client_name))
322 goto fail_unref;
325 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &port_name))
327 goto fail_unref;
330 return;
332 fail_unref:
333 dbus_message_unref(call_ptr->reply);
334 call_ptr->reply = NULL;
336 fail:
337 a2j_error("Ran out of memory trying to construct method return");
340 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(exit)
341 A2J_DBUS_METHOD_ARGUMENTS_END
343 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(start)
344 A2J_DBUS_METHOD_ARGUMENTS_END
346 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(stop)
347 A2J_DBUS_METHOD_ARGUMENTS_END
349 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(is_started)
350 A2J_DBUS_METHOD_ARGUMENT("started", DBUS_TYPE_BOOLEAN_AS_STRING, A2J_DBUS_DIRECTION_OUT)
351 A2J_DBUS_METHOD_ARGUMENTS_END
353 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(get_jack_client_name)
354 A2J_DBUS_METHOD_ARGUMENT("jack_client_name", DBUS_TYPE_STRING_AS_STRING, A2J_DBUS_DIRECTION_OUT)
355 A2J_DBUS_METHOD_ARGUMENTS_END
357 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(map_alsa_to_jack_port)
358 A2J_DBUS_METHOD_ARGUMENT("alsa_client_id", DBUS_TYPE_UINT32_AS_STRING, A2J_DBUS_DIRECTION_IN)
359 A2J_DBUS_METHOD_ARGUMENT("alsa_port_id", DBUS_TYPE_UINT32_AS_STRING, A2J_DBUS_DIRECTION_IN)
360 A2J_DBUS_METHOD_ARGUMENT("map_playback", DBUS_TYPE_BOOLEAN_AS_STRING, A2J_DBUS_DIRECTION_IN)
361 A2J_DBUS_METHOD_ARGUMENT("jack_port_name", DBUS_TYPE_STRING_AS_STRING, A2J_DBUS_DIRECTION_OUT)
362 A2J_DBUS_METHOD_ARGUMENTS_END
364 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(map_jack_port_to_alsa)
365 A2J_DBUS_METHOD_ARGUMENT("jack_port_name", "s", A2J_DBUS_DIRECTION_IN)
366 A2J_DBUS_METHOD_ARGUMENT("alsa_client_id", DBUS_TYPE_UINT32_AS_STRING, A2J_DBUS_DIRECTION_OUT)
367 A2J_DBUS_METHOD_ARGUMENT("alsa_port_id", DBUS_TYPE_UINT32_AS_STRING, A2J_DBUS_DIRECTION_OUT)
368 A2J_DBUS_METHOD_ARGUMENT("alsa_client_name", DBUS_TYPE_STRING_AS_STRING, A2J_DBUS_DIRECTION_OUT)
369 A2J_DBUS_METHOD_ARGUMENT("alsa_port_name", DBUS_TYPE_STRING_AS_STRING, A2J_DBUS_DIRECTION_OUT)
370 A2J_DBUS_METHOD_ARGUMENTS_END
372 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(set_hw_export)
373 A2J_DBUS_METHOD_ARGUMENT("hw_export", DBUS_TYPE_BOOLEAN_AS_STRING, A2J_DBUS_DIRECTION_IN)
374 A2J_DBUS_METHOD_ARGUMENTS_END
376 A2J_DBUS_METHOD_ARGUMENTS_BEGIN(get_hw_export)
377 A2J_DBUS_METHOD_ARGUMENT("hw_export", DBUS_TYPE_BOOLEAN_AS_STRING, A2J_DBUS_DIRECTION_OUT)
378 A2J_DBUS_METHOD_ARGUMENTS_END
380 A2J_DBUS_METHODS_BEGIN
381 A2J_DBUS_METHOD_DESCRIBE(exit, a2j_dbus_exit)
382 A2J_DBUS_METHOD_DESCRIBE(start, a2j_dbus_start)
383 A2J_DBUS_METHOD_DESCRIBE(stop, a2j_dbus_stop)
384 A2J_DBUS_METHOD_DESCRIBE(is_started, a2j_dbus_is_started)
385 A2J_DBUS_METHOD_DESCRIBE(get_jack_client_name, a2j_dbus_get_jack_client_name)
386 A2J_DBUS_METHOD_DESCRIBE(map_alsa_to_jack_port, a2j_dbus_map_alsa_to_jack_port)
387 A2J_DBUS_METHOD_DESCRIBE(map_jack_port_to_alsa, a2j_dbus_map_jack_port_to_alsa)
388 A2J_DBUS_METHOD_DESCRIBE(set_hw_export, a2j_dbus_set_hw_export)
389 A2J_DBUS_METHOD_DESCRIBE(get_hw_export, a2j_dbus_get_hw_export)
390 A2J_DBUS_METHODS_END
392 A2J_DBUS_SIGNAL_ARGUMENTS_BEGIN(bridge_started)
393 A2J_DBUS_SIGNAL_ARGUMENTS_END
395 A2J_DBUS_SIGNAL_ARGUMENTS_BEGIN(bridge_stopped)
396 A2J_DBUS_SIGNAL_ARGUMENTS_END
398 A2J_DBUS_SIGNALS_BEGIN
399 A2J_DBUS_SIGNAL_DESCRIBE(bridge_started)
400 A2J_DBUS_SIGNAL_DESCRIBE(bridge_stopped)
401 A2J_DBUS_SIGNALS_END
403 A2J_DBUS_IFACE_BEGIN(g_a2j_iface_control, INTERFACE_NAME)
404 A2J_DBUS_IFACE_EXPOSE_METHODS
405 A2J_DBUS_IFACE_EXPOSE_SIGNALS
406 A2J_DBUS_IFACE_END