ladishd: maintain list of currently running apps
[ladish.git] / jack_proxy.c
blobc792f308f9299f664bfae55b6add5eabaff13335
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains helper functionality for accessing JACK through D-Bus
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 //#define LADISH_DEBUG
29 #include "common.h"
30 #include "jack_proxy.h"
31 #include "dbus/helpers.h"
32 #include "dbus_constants.h"
34 jack_proxy_callback_server_started g_on_server_started;
35 jack_proxy_callback_server_stopped g_on_server_stopped;
36 jack_proxy_callback_server_appeared g_on_server_appeared;
37 jack_proxy_callback_server_disappeared g_on_server_disappeared;
39 static
40 void
41 on_jack_control_signal(
42 DBusMessage * message_ptr,
43 const char * signal_name)
45 if (strcmp(signal_name, "ServerStarted") == 0)
47 log_debug("JACK server start detected.");
48 if (g_on_server_started != NULL)
50 g_on_server_started();
53 return;
56 if (strcmp(signal_name, "ServerStopped") == 0)
58 log_debug("JACK server stop detected.");
59 if (g_on_server_stopped != NULL)
61 g_on_server_stopped();
64 return;
68 static
69 DBusHandlerResult
70 on_bus_signal(
71 DBusMessage * message_ptr,
72 const char * signal_name)
74 const char * object_name;
75 const char * old_owner;
76 const char * new_owner;
78 //log_info("bus signal '%s' received", signal_name);
80 dbus_error_init(&g_dbus_error);
82 if (strcmp(signal_name, "NameOwnerChanged") == 0)
84 //log_info("NameOwnerChanged signal received");
86 if (!dbus_message_get_args(
87 message_ptr,
88 &g_dbus_error,
89 DBUS_TYPE_STRING, &object_name,
90 DBUS_TYPE_STRING, &old_owner,
91 DBUS_TYPE_STRING, &new_owner,
92 DBUS_TYPE_INVALID)) {
93 log_error("Cannot get message arguments: %s", g_dbus_error.message);
94 dbus_error_free(&g_dbus_error);
95 return DBUS_HANDLER_RESULT_HANDLED;
98 if (strcmp(object_name, JACKDBUS_SERVICE_NAME) != 0)
100 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
103 if (old_owner[0] == '\0')
105 log_debug("JACK serivce appeared");
106 if (g_on_server_appeared != NULL)
108 g_on_server_appeared();
111 else if (new_owner[0] == '\0')
113 log_debug("JACK serivce disappeared");
114 if (g_on_server_disappeared != NULL)
116 g_on_server_disappeared();
120 return DBUS_HANDLER_RESULT_HANDLED;
123 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
126 static
127 DBusHandlerResult
128 dbus_signal_handler(
129 DBusConnection * connection_ptr,
130 DBusMessage * message_ptr,
131 void * data)
133 const char * object_path;
134 const char * interface;
135 const char * signal_name;
137 /* Non-signal messages are ignored */
138 if (dbus_message_get_type(message_ptr) != DBUS_MESSAGE_TYPE_SIGNAL)
140 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
143 interface = dbus_message_get_interface(message_ptr);
144 if (interface == NULL)
146 /* Signals with no interface are ignored */
147 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
150 object_path = dbus_message_get_path(message_ptr);
152 signal_name = dbus_message_get_member(message_ptr);
153 if (signal_name == NULL)
155 log_error("Received signal with NULL member");
156 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
159 log_debug("'%s' sent signal '%s'::'%s'", object_path, interface, signal_name);
161 /* Handle JACK patchbay and control interface signals */
162 if (object_path != NULL && strcmp(object_path, JACKDBUS_OBJECT_PATH) == 0)
164 if (strcmp(interface, JACKDBUS_IFACE_CONTROL) == 0)
166 on_jack_control_signal(message_ptr, signal_name);
167 return DBUS_HANDLER_RESULT_HANDLED;
170 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
173 /* Handle session bus signals to track JACK service alive state */
174 if (strcmp(interface, DBUS_INTERFACE_DBUS) == 0)
176 return on_bus_signal(message_ptr, signal_name);
179 /* Let everything else pass through */
180 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
183 bool
184 jack_proxy_init(
185 jack_proxy_callback_server_started server_started,
186 jack_proxy_callback_server_stopped server_stopped,
187 jack_proxy_callback_server_appeared server_appeared,
188 jack_proxy_callback_server_disappeared server_disappeared)
190 char rule[1024];
191 const char ** signal;
193 const char * control_signals[] = {
194 "ServerStarted",
195 "ServerStopped",
196 NULL};
198 dbus_bus_add_match(
199 g_dbus_connection,
200 "type='signal',interface='"DBUS_INTERFACE_DBUS"',member=NameOwnerChanged,arg0='"JACKDBUS_SERVICE_NAME"'",
201 &g_dbus_error);
202 if (dbus_error_is_set(&g_dbus_error))
204 log_error("Failed to add D-Bus match rule: %s", g_dbus_error.message);
205 dbus_error_free(&g_dbus_error);
206 return false;
209 for (signal = control_signals; *signal != NULL; signal++)
211 snprintf(
212 rule,
213 sizeof(rule),
214 "type='signal',sender='"JACKDBUS_SERVICE_NAME"',path='"JACKDBUS_OBJECT_PATH"',interface='"JACKDBUS_IFACE_CONTROL"',member='%s'",
215 *signal);
217 dbus_bus_add_match(g_dbus_connection, rule, &g_dbus_error);
218 if (dbus_error_is_set(&g_dbus_error))
220 log_error("Failed to add D-Bus match rule: %s", g_dbus_error.message);
221 dbus_error_free(&g_dbus_error);
222 return false;
226 if (!dbus_connection_add_filter(g_dbus_connection, dbus_signal_handler, NULL, NULL))
228 log_error("Failed to add D-Bus filter");
229 return false;
232 g_on_server_started = server_started;
233 g_on_server_stopped = server_stopped;
234 g_on_server_appeared = server_appeared;
235 g_on_server_disappeared = server_disappeared;
238 bool started;
240 if (jack_proxy_is_started(&started))
242 if (g_on_server_appeared != NULL)
244 g_on_server_appeared();
247 if (g_on_server_started != NULL && started)
249 g_on_server_started();
254 return true;
257 void
258 jack_proxy_uninit(
259 void)
261 dbus_connection_remove_filter(g_dbus_connection, dbus_signal_handler, NULL);
264 bool
265 jack_proxy_is_started(
266 bool * started_ptr)
268 dbus_bool_t started;
270 if (!dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "IsStarted", "", "b", &started))
272 return false;
275 *started_ptr = started;
276 return true;
279 bool
280 jack_proxy_get_client_pid(
281 uint64_t client_id,
282 pid_t * pid_ptr)
284 return false;
287 bool
288 jack_proxy_connect_ports(
289 uint64_t port1_id,
290 uint64_t port2_id)
292 return false;
295 static
296 bool
297 add_address(
298 DBusMessageIter * iter_ptr,
299 const char * address)
301 DBusMessageIter array_iter;
302 const char * component;
304 if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_ARRAY, "s", &array_iter))
306 log_error("dbus_message_iter_open_container() failed.");
307 return false;
310 if (address != NULL)
312 component = address;
313 while (*component != 0)
315 if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &component))
317 log_error("dbus_message_iter_append_basic() failed.");
318 return false;
321 component += strlen(component) + 1;
325 dbus_message_iter_close_container(iter_ptr, &array_iter);
327 return true;
330 bool
331 jack_proxy_read_conf_container(
332 const char * address,
333 void * callback_context,
334 bool (* callback)(void * context, bool leaf, const char * address, char * child))
336 DBusMessage * request_ptr;
337 DBusMessage * reply_ptr;
338 DBusMessageIter top_iter;
339 DBusMessageIter array_iter;
340 const char * reply_signature;
341 dbus_bool_t leaf; /* Whether children are parameters (true) or containers (false) */
342 char * child;
344 request_ptr = dbus_message_new_method_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONFIGURE, "ReadContainer");
345 if (request_ptr == NULL)
347 log_error("dbus_message_new_method_call() failed.");
348 return false;
351 dbus_message_iter_init_append(request_ptr, &top_iter);
353 if (!add_address(&top_iter, address))
355 dbus_message_unref(request_ptr);
356 return false;
359 // send message and get a handle for a reply
360 reply_ptr = dbus_connection_send_with_reply_and_block(
361 g_dbus_connection,
362 request_ptr,
363 DBUS_CALL_DEFAULT_TIMEOUT,
364 &g_dbus_error);
366 dbus_message_unref(request_ptr);
368 if (reply_ptr == NULL)
370 log_error("no reply from JACK server, error is '%s'", g_dbus_error.message);
371 dbus_error_free(&g_dbus_error);
372 return false;
375 reply_signature = dbus_message_get_signature(reply_ptr);
377 if (strcmp(reply_signature, "bas") != 0)
379 log_error("ReadContainer() reply signature mismatch. '%s'", reply_signature);
380 dbus_message_unref(reply_ptr);
381 return false;
384 dbus_message_iter_init(reply_ptr, &top_iter);
386 dbus_message_iter_get_basic(&top_iter, &leaf);
387 dbus_message_iter_next(&top_iter);
389 dbus_message_iter_recurse(&top_iter, &array_iter);
391 while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID)
393 dbus_message_iter_get_basic(&array_iter, &child);
395 if (!callback(callback_context, leaf, address, child))
397 break;
400 dbus_message_iter_next(&array_iter);
403 dbus_message_unref(reply_ptr);
405 return true;
408 bool
409 get_variant(
410 DBusMessageIter * iter_ptr,
411 struct jack_parameter_variant * parameter_ptr)
413 DBusMessageIter variant_iter;
414 int type;
415 dbus_bool_t boolean;
416 dbus_int32_t int32;
417 dbus_uint32_t uint32;
418 char * string;
420 dbus_message_iter_recurse(iter_ptr, &variant_iter);
421 log_debug("variant signature: '%s'", dbus_message_iter_get_signature(&variant_iter));
423 type = dbus_message_iter_get_arg_type(&variant_iter);
424 switch (type)
426 case DBUS_TYPE_INT32:
427 dbus_message_iter_get_basic(&variant_iter, &int32);
428 parameter_ptr->value.int32 = int32;
429 parameter_ptr->type = jack_int32;
430 return true;
431 case DBUS_TYPE_UINT32:
432 dbus_message_iter_get_basic(&variant_iter, &uint32);
433 parameter_ptr->value.uint32 = uint32;
434 parameter_ptr->type = jack_uint32;
435 return true;
436 case DBUS_TYPE_BYTE:
437 dbus_message_iter_get_basic(&variant_iter, &parameter_ptr->value.byte);
438 parameter_ptr->type = jack_byte;
439 return true;
440 case DBUS_TYPE_STRING:
441 dbus_message_iter_get_basic(&variant_iter, &string);
442 string = strdup(string);
443 if (string == NULL)
445 log_error("strdup failed.");
446 return false;
449 parameter_ptr->value.string = string;
450 parameter_ptr->type = jack_string;
451 return true;
452 case DBUS_TYPE_BOOLEAN:
453 dbus_message_iter_get_basic(&variant_iter, &boolean);
454 parameter_ptr->value.boolean = boolean;
455 parameter_ptr->type = jack_boolean;
456 return true;
459 log_error("Unknown D-Bus parameter type %i", (int)type);
460 return false;
463 bool
464 jack_proxy_get_parameter_value(
465 const char * address,
466 bool * is_set_ptr,
467 struct jack_parameter_variant * parameter_ptr)
469 DBusMessage * request_ptr;
470 DBusMessage * reply_ptr;
471 DBusMessageIter top_iter;
472 const char * reply_signature;
473 dbus_bool_t is_set;
474 struct jack_parameter_variant default_value;
476 request_ptr = dbus_message_new_method_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONFIGURE, "GetParameterValue");
477 if (request_ptr == NULL)
479 log_error("dbus_message_new_method_call() failed.");
480 return false;
483 dbus_message_iter_init_append(request_ptr, &top_iter);
485 if (!add_address(&top_iter, address))
487 dbus_message_unref(request_ptr);
488 return false;
491 // send message and get a handle for a reply
492 reply_ptr = dbus_connection_send_with_reply_and_block(
493 g_dbus_connection,
494 request_ptr,
495 DBUS_CALL_DEFAULT_TIMEOUT,
496 &g_dbus_error);
498 dbus_message_unref(request_ptr);
500 if (reply_ptr == NULL)
502 log_error("no reply from JACK server, error is '%s'", g_dbus_error.message);
503 dbus_error_free(&g_dbus_error);
504 return false;
507 reply_signature = dbus_message_get_signature(reply_ptr);
509 if (strcmp(reply_signature, "bvv") != 0)
511 log_error("GetParameterValue() reply signature mismatch. '%s'", reply_signature);
512 dbus_message_unref(reply_ptr);
513 return false;
516 dbus_message_iter_init(reply_ptr, &top_iter);
518 dbus_message_iter_get_basic(&top_iter, &is_set);
519 dbus_message_iter_next(&top_iter);
521 if (!get_variant(&top_iter, &default_value))
523 dbus_message_unref(reply_ptr);
524 return false;
527 if (default_value.type == jack_string)
529 free(default_value.value.string);
532 dbus_message_iter_next(&top_iter);
534 if (!get_variant(&top_iter, parameter_ptr))
536 dbus_message_unref(reply_ptr);
537 return false;
540 dbus_message_unref(reply_ptr);
542 *is_set_ptr = is_set;
544 return true;
547 bool
548 jack_proxy_set_parameter_value(
549 const char * address,
550 const struct jack_parameter_variant * parameter_ptr)
552 DBusMessage * request_ptr;
553 DBusMessage * reply_ptr;
554 DBusMessageIter top_iter;
555 const char * reply_signature;
556 int type;
557 const void * value_ptr;
558 dbus_bool_t boolean;
560 switch (parameter_ptr->type)
562 case jack_int32:
563 type = DBUS_TYPE_INT32;
564 value_ptr = &parameter_ptr->value.int32;
565 break;
566 case jack_uint32:
567 type = DBUS_TYPE_UINT32;
568 value_ptr = &parameter_ptr->value.uint32;
569 break;
570 case jack_byte:
571 type = DBUS_TYPE_BYTE;
572 value_ptr = &parameter_ptr->value.byte;
573 break;
574 case jack_string:
575 type = DBUS_TYPE_STRING;
576 value_ptr = &parameter_ptr->value.string;
577 break;
578 case jack_boolean:
579 type = DBUS_TYPE_BOOLEAN;
580 boolean = parameter_ptr->value.boolean;
581 value_ptr = &boolean;
582 break;
583 default:
584 log_error("Unknown jack parameter type %i", (int)parameter_ptr->type);
585 return false;
588 request_ptr = dbus_message_new_method_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONFIGURE, "SetParameterValue");
589 if (request_ptr == NULL)
591 log_error("dbus_message_new_method_call() failed.");
592 return false;
595 dbus_message_iter_init_append(request_ptr, &top_iter);
597 if (!add_address(&top_iter, address))
599 dbus_message_unref(request_ptr);
600 return false;
603 if (!dbus_iter_append_variant(&top_iter, type, value_ptr))
605 dbus_message_unref(request_ptr);
606 return false;
609 // send message and get a handle for a reply
610 reply_ptr = dbus_connection_send_with_reply_and_block(
611 g_dbus_connection,
612 request_ptr,
613 DBUS_CALL_DEFAULT_TIMEOUT,
614 &g_dbus_error);
616 dbus_message_unref(request_ptr);
618 if (reply_ptr == NULL)
620 log_error("no reply from JACK server, error is '%s'", g_dbus_error.message);
621 dbus_error_free(&g_dbus_error);
622 return false;
625 reply_signature = dbus_message_get_signature(reply_ptr);
627 dbus_message_unref(reply_ptr);
629 if (strcmp(reply_signature, "") != 0)
631 log_error("SetParameterValue() reply signature mismatch. '%s'", reply_signature);
632 return false;
635 return true;
638 bool
639 jack_proxy_reset_parameter_value(
640 const char * address)
642 DBusMessage * request_ptr;
643 DBusMessage * reply_ptr;
644 DBusMessageIter top_iter;
645 const char * reply_signature;
647 request_ptr = dbus_message_new_method_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONFIGURE, "ResetParameterValue");
648 if (request_ptr == NULL)
650 log_error("dbus_message_new_method_call() failed.");
651 return false;
654 dbus_message_iter_init_append(request_ptr, &top_iter);
656 if (!add_address(&top_iter, address))
658 dbus_message_unref(request_ptr);
659 return false;
662 // send message and get a handle for a reply
663 reply_ptr = dbus_connection_send_with_reply_and_block(
664 g_dbus_connection,
665 request_ptr,
666 DBUS_CALL_DEFAULT_TIMEOUT,
667 &g_dbus_error);
669 dbus_message_unref(request_ptr);
671 if (reply_ptr == NULL)
673 log_error("no reply from JACK server, error is '%s'", g_dbus_error.message);
674 dbus_error_free(&g_dbus_error);
675 return false;
678 reply_signature = dbus_message_get_signature(reply_ptr);
680 dbus_message_unref(reply_ptr);
682 if (strcmp(reply_signature, "") != 0)
684 log_error("ResetParameterValue() reply signature mismatch. '%s'", reply_signature);
685 return false;
688 return true;
691 bool jack_proxy_start_server(void)
693 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "StartServer", "", "");
696 bool jack_proxy_stop_server(void)
698 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "StopServer", "", "");
701 bool jack_proxy_is_realtime(bool * realtime_ptr)
703 dbus_bool_t realtime;
705 if (!dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "IsStarted", "", "b", &realtime))
707 return false;
710 *realtime_ptr = realtime;
711 return true;
714 bool jack_proxy_sample_rate(uint32_t * sample_rate_ptr)
716 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "GetSampleRate", "", "u", sample_rate_ptr);
719 bool jack_proxy_get_xruns(uint32_t * xruns_ptr)
721 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "GetXruns", "", "u", xruns_ptr);
724 bool jack_proxy_get_dsp_load(double * dsp_load_ptr)
726 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "GetLoad", "", "d", dsp_load_ptr);
729 bool jack_proxy_get_buffer_size(uint32_t * size_ptr)
731 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "GetBufferSize", "", "u", size_ptr);
734 bool jack_proxy_set_buffer_size(uint32_t size)
736 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "SetBufferSize", "u", &size, "");
739 bool jack_proxy_reset_xruns(void)
741 return dbus_call(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, JACKDBUS_IFACE_CONTROL, "ResetXruns", "", "");
744 static
745 bool
746 reset_callback(
747 void * context,
748 bool leaf,
749 const char * address,
750 char * child)
752 const char * component;
753 char * dst;
754 size_t len;
756 component = address;
757 while (*component != 0)
759 component += strlen(component) + 1;
762 /* address always is same buffer as the one in the jack_reset_all_params() stack */
763 dst = (char *)component;
765 len = strlen(child) + 1;
766 memcpy(dst, child, len);
767 dst[len] = 0;
769 if (leaf)
771 if (!jack_proxy_reset_parameter_value(address))
773 log_error("cannot reset value of parameter");
774 return false;
777 else
779 if (!jack_proxy_read_conf_container(address, context, reset_callback))
781 log_error("cannot read container");
782 return false;
786 *dst = 0;
788 return true;
791 bool jack_reset_all_params(void)
793 char address[1024] = "";
795 return jack_proxy_read_conf_container(address, NULL, reset_callback);