1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of the studio singleton object
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.
28 #include "../jack_proxy.h"
30 #include "../dbus_constants.h"
33 extern const interface_t g_interface_studio
;
37 /* this must be first member of struct studio because object_path_new() assumes all interfaces have same context */
38 struct patchbay_implementator patchbay_impl
;
40 struct list_head all_connections
; /* All connections (studio guts and all rooms). Including superconnections. */
41 struct list_head all_ports
; /* All ports (studio guts and all rooms) */
42 struct list_head all_clients
; /* All clients (studio guts and all rooms) */
43 struct list_head jack_connections
; /* JACK connections (studio guts and all rooms). Including superconnections, excluding virtual connections. */
44 struct list_head jack_ports
; /* JACK ports (studio guts and all rooms). Excluding virtual ports. */
45 struct list_head jack_clients
; /* JACK clients (studio guts and all rooms). Excluding virtual clients. */
46 struct list_head rooms
; /* Rooms connected to the studio */
47 struct list_head clients
; /* studio clients (studio guts and room links) */
48 struct list_head ports
; /* studio ports (studio guts and room links) */
50 bool persisted
:1; /* Studio has on-disk representation, i.e. can be reloaded from disk */
51 bool modified
:1; /* Studio needs saving */
52 bool jack_conf_valid
:1; /* JACK server configuration obtained successfully */
54 struct list_head jack_conf
; /* root of the conf tree */
55 struct list_head jack_params
; /* list of conf tree leaves */
57 object_path_t
* dbus_object
;
59 struct list_head event_queue
;
62 #define EVENT_JACK_START 0
63 #define EVENT_JACK_STOP 1
67 struct list_head siblings
;
71 #define JACK_CONF_MAX_ADDRESS_SIZE 1024
73 struct jack_conf_parameter
75 struct list_head siblings
; /* siblings in container children list */
76 struct list_head leaves
; /* studio::jack_param siblings */
78 struct jack_conf_container
* parent_ptr
;
79 char address
[JACK_CONF_MAX_ADDRESS_SIZE
];
80 struct jack_parameter_variant parameter
;
83 struct jack_conf_container
85 struct list_head siblings
;
87 struct jack_conf_container
* parent_ptr
;
88 bool children_leafs
; /* if true, children are "jack_conf_parameter"s, if false, children are "jack_conf_container"s */
89 struct list_head children
;
92 struct conf_callback_context
94 char address
[JACK_CONF_MAX_ADDRESS_SIZE
];
95 struct list_head
* container_ptr
;
96 struct jack_conf_container
* parent_ptr
;
100 jack_conf_container_create(
101 struct jack_conf_container
** container_ptr_ptr
,
104 struct jack_conf_container
* container_ptr
;
106 container_ptr
= malloc(sizeof(struct jack_conf_container
));
107 if (container_ptr
== NULL
)
109 lash_error("malloc() failed to allocate struct jack_conf_container");
113 container_ptr
->name
= strdup(name
);
114 if (container_ptr
->name
== NULL
)
116 lash_error("strdup() failed to duplicate \"%s\"", name
);
120 INIT_LIST_HEAD(&container_ptr
->children
);
121 container_ptr
->children_leafs
= false;
123 *container_ptr_ptr
= container_ptr
;
134 jack_conf_parameter_create(
135 struct jack_conf_parameter
** parameter_ptr_ptr
,
138 struct jack_conf_parameter
* parameter_ptr
;
140 parameter_ptr
= malloc(sizeof(struct jack_conf_parameter
));
141 if (parameter_ptr
== NULL
)
143 lash_error("malloc() failed to allocate struct jack_conf_parameter");
147 parameter_ptr
->name
= strdup(name
);
148 if (parameter_ptr
->name
== NULL
)
150 lash_error("strdup() failed to duplicate \"%s\"", name
);
154 *parameter_ptr_ptr
= parameter_ptr
;
165 jack_conf_parameter_destroy(
166 struct jack_conf_parameter
* parameter_ptr
)
169 lash_info("jack_conf_parameter destroy");
171 switch (parameter_ptr
->parameter
.type
)
174 lash_info("%s value is %s (boolean)", parameter_ptr
->name
, parameter_ptr
->parameter
.value
.boolean
? "true" : "false");
177 lash_info("%s value is %s (string)", parameter_ptr
->name
, parameter_ptr
->parameter
.value
.string
);
180 lash_info("%s value is %u/%c (byte/char)", parameter_ptr
->name
, parameter_ptr
->parameter
.value
.byte
, (char)parameter_ptr
->parameter
.value
.byte
);
183 lash_info("%s value is %u (uint32)", parameter_ptr
->name
, (unsigned int)parameter_ptr
->parameter
.value
.uint32
);
186 lash_info("%s value is %u (int32)", parameter_ptr
->name
, (signed int)parameter_ptr
->parameter
.value
.int32
);
189 lash_error("unknown jack parameter_ptr->parameter type %d (%s)", (int)parameter_ptr
->parameter
.type
, parameter_ptr
->name
);
194 if (parameter_ptr
->parameter
.type
== jack_string
)
196 free(parameter_ptr
->parameter
.value
.string
);
199 free(parameter_ptr
->name
);
204 jack_conf_container_destroy(
205 struct jack_conf_container
* container_ptr
)
207 struct list_head
* node_ptr
;
209 //lash_info("\"%s\" jack_conf_parameter destroy", container_ptr->name);
211 if (!container_ptr
->children_leafs
)
213 while (!list_empty(&container_ptr
->children
))
215 node_ptr
= container_ptr
->children
.next
;
217 jack_conf_container_destroy(list_entry(node_ptr
, struct jack_conf_container
, siblings
));
222 while (!list_empty(&container_ptr
->children
))
224 node_ptr
= container_ptr
->children
.next
;
226 jack_conf_parameter_destroy(list_entry(node_ptr
, struct jack_conf_parameter
, siblings
));
230 free(container_ptr
->name
);
234 #define context_ptr ((struct conf_callback_context *)context)
241 const char * address
,
244 char path
[JACK_CONF_MAX_ADDRESS_SIZE
];
245 const char * component
;
249 struct jack_conf_container
* parent_ptr
;
250 struct jack_conf_container
* container_ptr
;
251 struct jack_conf_parameter
* parameter_ptr
;
253 parent_ptr
= context_ptr
->parent_ptr
;
257 while (*component
!= 0)
259 len
= strlen(component
);
260 memcpy(dst
, component
, len
);
262 component
+= len
+ 1;
268 /* address always is same buffer as the one supplied through context pointer */
269 assert(context_ptr
->address
== address
);
270 dst
= (char *)component
;
272 len
= strlen(child
) + 1;
273 memcpy(dst
, child
, len
);
278 lash_debug("%s (leaf)", path
);
280 if (parent_ptr
== NULL
)
282 lash_error("jack conf parameters can't appear in root container");
286 if (!parent_ptr
->children_leafs
)
288 if (!list_empty(&parent_ptr
->children
))
290 lash_error("jack conf parameters cant be mixed with containers at same hierarchy level");
294 parent_ptr
->children_leafs
= true;
297 if (!jack_conf_parameter_create(¶meter_ptr
, child
))
299 lash_error("jack_conf_parameter_create() failed");
303 if (!jack_proxy_get_parameter_value(context_ptr
->address
, &is_set
, ¶meter_ptr
->parameter
))
305 lash_error("cannot get value of %s", path
);
311 switch (parameter_ptr
->parameter
.type
)
314 lash_info("%s value is %s (boolean)", path
, parameter_ptr
->parameter
.value
.boolean
? "true" : "false");
317 lash_info("%s value is %s (string)", path
, parameter_ptr
->parameter
.value
.string
);
320 lash_info("%s value is %u/%c (byte/char)", path
, parameter_ptr
->parameter
.value
.byte
, (char)parameter_ptr
->parameter
.value
.byte
);
323 lash_info("%s value is %u (uint32)", path
, (unsigned int)parameter_ptr
->parameter
.value
.uint32
);
326 lash_info("%s value is %u (int32)", path
, (signed int)parameter_ptr
->parameter
.value
.int32
);
329 lash_error("unknown jack parameter_ptr->parameter type %d (%s)", (int)parameter_ptr
->parameter
.type
, path
);
330 jack_conf_parameter_destroy(parameter_ptr
);
334 parameter_ptr
->parent_ptr
= parent_ptr
;
335 memcpy(parameter_ptr
->address
, context_ptr
->address
, JACK_CONF_MAX_ADDRESS_SIZE
);
336 list_add_tail(¶meter_ptr
->siblings
, &parent_ptr
->children
);
337 list_add_tail(¶meter_ptr
->leaves
, &g_studio
.jack_params
);
341 jack_conf_parameter_destroy(parameter_ptr
);
346 lash_debug("%s (container)", path
);
348 if (parent_ptr
!= NULL
&& parent_ptr
->children_leafs
)
350 lash_error("jack conf containers cant be mixed with parameters at same hierarchy level");
354 if (!jack_conf_container_create(&container_ptr
, child
))
356 lash_error("jack_conf_container_create() failed");
360 container_ptr
->parent_ptr
= parent_ptr
;
362 if (parent_ptr
== NULL
)
364 list_add_tail(&container_ptr
->siblings
, &g_studio
.jack_conf
);
368 list_add_tail(&container_ptr
->siblings
, &parent_ptr
->children
);
371 context_ptr
->parent_ptr
= container_ptr
;
373 if (!jack_proxy_read_conf_container(context_ptr
->address
, context
, conf_callback
))
375 lash_error("cannot read container %s", path
);
379 context_ptr
->parent_ptr
= parent_ptr
;
389 bool studio_fetch_jack_settings()
391 struct conf_callback_context context
;
393 context
.address
[0] = 0;
394 context
.container_ptr
= &g_studio
.jack_conf
;
395 context
.parent_ptr
= NULL
;
397 if (!jack_proxy_read_conf_container(context
.address
, &context
, conf_callback
))
399 lash_error("jack_proxy_read_conf_container() failed.");
407 studio_activate(void)
409 object_path_t
* object
;
411 object
= object_path_new(STUDIO_OBJECT_PATH
, &g_studio
, 2, &g_interface_studio
, &g_interface_patchbay
);
414 lash_error("object_path_new() failed");
418 if (!object_path_register(g_dbus_connection
, object
))
420 lash_error("object_path_register() failed");
421 object_path_destroy(g_dbus_connection
, object
);
425 lash_info("Studio D-Bus object created.");
427 g_studio
.dbus_object
= object
;
429 emit_studio_appeared();
437 struct list_head
* node_ptr
;
439 g_studio
.modified
= false;
440 g_studio
.persisted
= false;
442 while (!list_empty(&g_studio
.jack_conf
))
444 node_ptr
= g_studio
.jack_conf
.next
;
446 jack_conf_container_destroy(list_entry(node_ptr
, struct jack_conf_container
, siblings
));
449 g_studio
.jack_conf_valid
= false;
451 if (g_studio
.dbus_object
!= NULL
)
453 object_path_destroy(g_dbus_connection
, g_studio
.dbus_object
);
454 g_studio
.dbus_object
= NULL
;
455 emit_studio_disappeared();
460 studio_clear_if_not_persisted(void)
462 if (!g_studio
.persisted
)
469 void on_event_jack_started(void)
471 if (g_studio
.dbus_object
== NULL
)
476 if (!studio_fetch_jack_settings(g_studio
))
478 lash_error("studio_fetch_jack_settings() failed.");
480 studio_clear_if_not_persisted();
484 lash_info("jack conf successfully retrieved");
485 g_studio
.jack_conf_valid
= true;
488 void on_event_jack_stopped(void)
490 studio_clear_if_not_persisted();
492 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
495 void studio_run(void)
497 struct event
* event_ptr
;
499 while (!list_empty(&g_studio
.event_queue
))
501 event_ptr
= list_entry(g_studio
.event_queue
.next
, struct event
, siblings
);
502 list_del(g_studio
.event_queue
.next
);
504 switch (event_ptr
->type
)
506 case EVENT_JACK_START
:
507 on_event_jack_started();
509 case EVENT_JACK_STOP
:
510 on_event_jack_stopped();
518 static void on_jack_server_started(void)
520 struct event
* event_ptr
;
522 lash_info("JACK server start detected.");
524 event_ptr
= malloc(sizeof(struct event
));
525 if (event_ptr
== NULL
)
527 lash_error("malloc() failed to allocate struct event. Ignoring JACK start.");
531 event_ptr
->type
= EVENT_JACK_START
;
532 list_add_tail(&event_ptr
->siblings
, &g_studio
.event_queue
);
535 static void on_jack_server_stopped(void)
537 struct event
* event_ptr
;
539 lash_info("JACK server stop detected.");
541 event_ptr
= malloc(sizeof(struct event
));
542 if (event_ptr
== NULL
)
544 lash_error("malloc() failed to allocate struct event. Ignoring JACK stop.");
548 event_ptr
->type
= EVENT_JACK_STOP
;
549 list_add_tail(&event_ptr
->siblings
, &g_studio
.event_queue
);
552 static void on_jack_server_appeared(void)
554 lash_info("JACK controller appeared.");
557 static void on_jack_server_disappeared(void)
559 lash_info("JACK controller disappeared.");
562 #define studio_ptr ((struct studio *)this)
565 studio_get_graph_version(
568 //lash_info("studio_get_graph_version() called");
574 bool studio_init(void)
576 lash_info("studio object construct");
578 g_studio
.patchbay_impl
.this = &g_studio
;
579 g_studio
.patchbay_impl
.get_graph_version
= studio_get_graph_version
;
581 INIT_LIST_HEAD(&g_studio
.all_connections
);
582 INIT_LIST_HEAD(&g_studio
.all_ports
);
583 INIT_LIST_HEAD(&g_studio
.all_clients
);
584 INIT_LIST_HEAD(&g_studio
.jack_connections
);
585 INIT_LIST_HEAD(&g_studio
.jack_ports
);
586 INIT_LIST_HEAD(&g_studio
.jack_clients
);
587 INIT_LIST_HEAD(&g_studio
.rooms
);
588 INIT_LIST_HEAD(&g_studio
.clients
);
589 INIT_LIST_HEAD(&g_studio
.ports
);
591 INIT_LIST_HEAD(&g_studio
.jack_conf
);
592 INIT_LIST_HEAD(&g_studio
.jack_params
);
594 INIT_LIST_HEAD(&g_studio
.event_queue
);
596 g_studio
.dbus_object
= NULL
;
599 if (!jack_proxy_init(
600 on_jack_server_started
,
601 on_jack_server_stopped
,
602 on_jack_server_appeared
,
603 on_jack_server_disappeared
))
611 void studio_uninit(void)
617 lash_info("studio object destroy");
620 bool studio_is_loaded(void)
622 return g_studio
.dbus_object
!= NULL
;
625 bool studio_save(const char * file_path
)
627 struct list_head
* node_ptr
;
628 struct jack_conf_parameter
* parameter_ptr
;
629 char path
[JACK_CONF_MAX_ADDRESS_SIZE
* 3]; /* encode each char in three bytes (percent encoding) */
632 static char hex_digits
[] = "0123456789ABCDEF";
634 lash_info("saving studio...");
636 list_for_each(node_ptr
, &g_studio
.jack_params
)
638 parameter_ptr
= list_entry(node_ptr
, struct jack_conf_parameter
, leaves
);
640 /* compose the parameter path, percent-encode "bad" chars */
641 src
= parameter_ptr
->address
;
650 case '/': /* used as separator for address components */
651 case '<': /* invalid attribute value char (XML spec) */
652 case '&': /* invalid attribute value char (XML spec) */
653 case '"': /* we store attribute values in double quotes - invalid attribute value char (XML spec) */
656 dst
[1] = hex_digits
[*src
>> 4];
657 dst
[2] = hex_digits
[*src
& 0x0F];
670 switch (parameter_ptr
->parameter
.type
)
673 lash_info("%s value is %s (boolean)", path
, parameter_ptr
->parameter
.value
.boolean
? "true" : "false");
676 lash_info("%s value is %s (string)", path
, parameter_ptr
->parameter
.value
.string
);
679 lash_info("%s value is %u/%c (byte/char)", path
, parameter_ptr
->parameter
.value
.byte
, (char)parameter_ptr
->parameter
.value
.byte
);
682 lash_info("%s value is %u (uint32)", path
, (unsigned int)parameter_ptr
->parameter
.value
.uint32
);
685 lash_info("%s value is %u (int32)", path
, (signed int)parameter_ptr
->parameter
.value
.int32
);
688 lash_error("unknown jack parameter_ptr->parameter type %d (%s)", (int)parameter_ptr
->parameter
.type
, path
);
693 return false; /* not implemented yet */
696 bool studio_load(const char * file_path
)
698 return false; /* not implemented yet */
701 static void ladish_save_studio(method_call_t
* call_ptr
)
703 //studio_save(g_studio, NULL)
706 METHOD_ARGS_BEGIN(Save
, "Save studio")
710 METHOD_DESCRIBE(Save
, ladish_save_studio
)
713 SIGNAL_ARGS_BEGIN(RoomAppeared
, "Room D-Bus object appeared")
714 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
717 SIGNAL_ARGS_BEGIN(RoomDisappeared
, "Room D-Bus object disappeared")
718 SIGNAL_ARG_DESCRIBE("room_path", "s", "room object path")
722 SIGNAL_DESCRIBE(RoomAppeared
)
723 SIGNAL_DESCRIBE(RoomDisappeared
)
726 INTERFACE_BEGIN(g_interface_studio
, IFACE_STUDIO
)
727 INTERFACE_DEFAULT_HANDLER
728 INTERFACE_EXPOSE_METHODS
729 INTERFACE_EXPOSE_SIGNALS