1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009, 2010 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains part of the studio singleton object implementation
9 * Other parts are in the other studio*.c files in same directory.
10 **************************************************************************
12 * LADI Session Handler is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * LADI Session Handler is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
24 * or write to the Free Software Foundation, Inc.,
25 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <sys/types.h>
35 #include "studio_internal.h"
36 #include "../dbus_constants.h"
38 #include "../catdup.h"
39 #include "dirhelpers.h"
40 #include "graph_dict.h"
43 #include "../proxies/notify_proxy.h"
45 #define STUDIOS_DIR "/studios/"
48 struct studio g_studio
;
50 bool ladish_studio_name_generate(char ** name_ptr
)
53 char timestamp_str
[26];
57 //ctime_r(&now, timestamp_str);
58 //timestamp_str[24] = 0;
59 snprintf(timestamp_str
, sizeof(timestamp_str
), "%llu", (unsigned long long)now
);
61 name
= catdup("Studio ", timestamp_str
);
64 log_error("catdup failed to create studio name");
72 bool ladish_studio_show(void)
74 dbus_object_path object
;
76 ASSERT(g_studio
.name
!= NULL
);
77 ASSERT(!g_studio
.announced
);
79 object
= dbus_object_path_new(
81 &g_interface_studio
, &g_studio
,
82 &g_interface_patchbay
, ladish_graph_get_dbus_context(g_studio
.studio_graph
),
83 &g_iface_graph_dict
, g_studio
.studio_graph
,
84 &g_iface_app_supervisor
, g_studio
.app_supervisor
,
88 log_error("dbus_object_path_new() failed");
92 if (!dbus_object_path_register(g_dbus_connection
, object
))
94 log_error("object_path_register() failed");
95 dbus_object_path_destroy(g_dbus_connection
, object
);
99 log_info("Studio D-Bus object created. \"%s\"", g_studio
.name
);
101 g_studio
.dbus_object
= object
;
103 emit_studio_appeared();
108 void ladish_studio_announce(void)
110 ASSERT(!g_studio
.announced
);
111 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL
, "Studio loaded", NULL
);
112 g_studio
.announced
= true;
115 bool ladish_studio_publish(void)
117 if (!ladish_studio_show())
122 ladish_studio_announce();
126 void ladish_studio_clear(void)
128 ladish_graph_dump(g_studio
.studio_graph
);
129 ladish_graph_dump(g_studio
.jack_graph
);
131 /* remove rooms that own clients in studio graph before clearing it */
132 ladish_studio_remove_all_rooms();
134 ladish_graph_clear(g_studio
.studio_graph
, NULL
);
135 ladish_graph_clear(g_studio
.jack_graph
, NULL
);
137 ladish_studio_jack_conf_clear();
139 g_studio
.modified
= false;
140 g_studio
.persisted
= false;
142 if (g_studio
.dbus_object
!= NULL
)
144 dbus_object_path_destroy(g_dbus_connection
, g_studio
.dbus_object
);
145 g_studio
.dbus_object
= NULL
;
146 emit_studio_disappeared();
149 if (g_studio
.announced
)
151 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL
, "Studio unloaded", NULL
);
152 g_studio
.announced
= false;
155 if (g_studio
.name
!= NULL
)
158 g_studio
.name
= NULL
;
161 if (g_studio
.filename
!= NULL
)
163 free(g_studio
.filename
);
164 g_studio
.filename
= NULL
;
167 ladish_app_supervisor_clear(g_studio
.app_supervisor
);
170 void ladish_studio_emit_started(void)
172 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioStarted", "");
175 void ladish_studio_emit_crashed(void)
177 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioCrashed", "");
180 void ladish_studio_emit_stopped(void)
182 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioStopped", "");
185 static bool ladish_studio_fill_room_info(DBusMessageIter
* iter_ptr
, ladish_room_handle room
)
187 DBusMessageIter dict_iter
;
189 uuid_t template_uuid
;
190 ladish_room_handle
template;
191 const char * template_name
;
194 name
= ladish_room_get_name(room
);
195 opath
= ladish_room_get_opath(room
);
197 if (!ladish_room_get_template_uuid(room
, template_uuid
))
200 template_name
= NULL
;
204 template = find_room_template_by_uuid(template_uuid
);
205 if (template != NULL
)
207 template_name
= ladish_room_get_name(template);
211 template_name
= NULL
;
215 if (!dbus_message_iter_append_basic(iter_ptr
, DBUS_TYPE_STRING
, &opath
))
217 log_error("dbus_message_iter_append_basic() failed.");
221 if (!dbus_message_iter_open_container(iter_ptr
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
))
223 log_error("dbus_message_iter_open_container() failed.");
227 if (!dbus_maybe_add_dict_entry_string(&dict_iter
, "template", template_name
))
229 log_error("dbus_maybe_add_dict_entry_string() failed.");
233 if (!dbus_maybe_add_dict_entry_string(&dict_iter
, "name", name
))
235 log_error("dbus_maybe_add_dict_entry_string() failed.");
239 if (!dbus_message_iter_close_container(iter_ptr
, &dict_iter
))
241 log_error("dbus_message_iter_close_container() failed.");
248 static void ladish_studio_emit_room_appeared(ladish_room_handle room
)
250 DBusMessage
* message_ptr
;
251 DBusMessageIter iter
;
253 message_ptr
= dbus_message_new_signal(STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "RoomAppeared");
254 if (message_ptr
== NULL
)
256 log_error("dbus_message_new_signal() failed.");
260 dbus_message_iter_init_append(message_ptr
, &iter
);
262 if (ladish_studio_fill_room_info(&iter
, room
))
264 dbus_signal_send(g_dbus_connection
, message_ptr
);
267 dbus_message_unref(message_ptr
);
270 void ladish_studio_emit_room_disappeared(ladish_room_handle room
)
272 DBusMessage
* message_ptr
;
273 DBusMessageIter iter
;
275 message_ptr
= dbus_message_new_signal(STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "RoomDisappeared");
276 if (message_ptr
== NULL
)
278 log_error("dbus_message_new_signal() failed.");
282 dbus_message_iter_init_append(message_ptr
, &iter
);
284 if (ladish_studio_fill_room_info(&iter
, room
))
286 dbus_signal_send(g_dbus_connection
, message_ptr
);
289 dbus_message_unref(message_ptr
);
292 void ladish_studio_room_appeared(ladish_room_handle room
)
294 log_info("Room \"%s\" appeared", ladish_room_get_name(room
));
295 list_add_tail(ladish_room_get_list_node(room
), &g_studio
.rooms
);
296 ladish_studio_emit_room_appeared(room
);
299 void ladish_studio_room_disappeared(ladish_room_handle room
)
301 log_info("Room \"%s\" disappeared", ladish_room_get_name(room
));
302 list_del(ladish_room_get_list_node(room
));
303 ladish_studio_emit_room_disappeared(room
);
307 ladish_studio_set_graph_connection_handlers(
309 ladish_graph_handle graph
,
310 ladish_app_supervisor_handle app_supervisor
)
312 ladish_virtualizer_set_graph_connection_handlers(context
, graph
);
313 return true; /* iterate all graphs */
316 void ladish_studio_on_event_jack_started(void)
318 if (!ladish_studio_fetch_jack_settings())
320 log_error("studio_fetch_jack_settings() failed.");
325 log_info("jack conf successfully retrieved");
326 g_studio
.jack_conf_valid
= true;
328 if (!graph_proxy_create(JACKDBUS_SERVICE_NAME
, JACKDBUS_OBJECT_PATH
, false, &g_studio
.jack_graph_proxy
))
330 log_error("graph_proxy_create() failed for jackdbus");
334 if (!ladish_virtualizer_create(g_studio
.jack_graph_proxy
, g_studio
.jack_graph
, &g_studio
.virtualizer
))
336 log_error("ladish_virtualizer_create() failed.");
340 ladish_studio_iterate_virtual_graphs(g_studio
.virtualizer
, ladish_studio_set_graph_connection_handlers
);
343 if (!graph_proxy_activate(g_studio
.jack_graph_proxy
))
345 log_error("graph_proxy_activate() failed.");
349 ladish_app_supervisor_autorun(g_studio
.app_supervisor
);
351 ladish_studio_emit_started();
352 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL
, "Studio started", NULL
);
355 static void ladish_studio_on_jack_stopped_internal(void)
357 if (g_studio
.virtualizer
)
359 ladish_virtualizer_destroy(g_studio
.virtualizer
);
360 g_studio
.virtualizer
= NULL
;
363 if (g_studio
.jack_graph_proxy
)
365 graph_proxy_destroy(g_studio
.jack_graph_proxy
);
366 g_studio
.jack_graph_proxy
= NULL
;
370 void ladish_studio_on_event_jack_stopped(void)
372 ladish_studio_emit_stopped();
373 ladish_studio_on_jack_stopped_internal();
374 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL
, "Studio stopped", NULL
);
377 void ladish_studio_handle_unexpected_jack_server_stop(void)
379 ladish_studio_emit_crashed();
380 ladish_studio_on_jack_stopped_internal();
382 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
385 static bool ladish_studio_hide_vgraph_non_virtual(void * context
, ladish_graph_handle graph
, ladish_app_supervisor_handle app_supervisor
)
387 ladish_graph_hide_non_virtual(graph
);
388 return true; /* iterate all vgraphs */
391 void ladish_studio_run(void)
395 ladish_cqueue_run(&g_studio
.cmd_queue
);
397 { /* if quit is requested, don't bother to process external events */
401 if (ladish_environment_consume_change(&g_studio
.env_store
, ladish_environment_jack_server_started
, &state
))
403 ladish_cqueue_clear(&g_studio
.cmd_queue
);
407 ladish_environment_ignore(&g_studio
.env_store
, ladish_environment_jack_server_present
);
409 if (g_studio
.jack_graph_proxy
!= NULL
)
411 log_error("Ignoring \"JACK started\" notification because it is already known that JACK is currently started.");
415 /* Automatic studio creation on JACK server start */
416 if (g_studio
.dbus_object
== NULL
)
418 ASSERT(g_studio
.name
== NULL
);
419 if (!ladish_studio_name_generate(&g_studio
.name
))
421 log_error("studio_name_generate() failed.");
425 g_studio
.automatic
= true;
427 ladish_studio_publish();
430 ladish_studio_on_event_jack_started();
434 /* JACK stopped but this was not expected. When expected.
435 * the change will be consumed by the run method of the studio stop command */
437 if (g_studio
.jack_graph_proxy
== NULL
)
439 log_error("Ignoring \"JACK stopped\" notification because it is already known that JACK is currently stopped.");
443 if (g_studio
.automatic
)
445 log_info("Unloading automatic studio.");
446 ladish_command_unload_studio(NULL
, &g_studio
.cmd_queue
);
448 ladish_studio_on_event_jack_stopped();
452 log_error("JACK stopped unexpectedly.");
453 log_error("Save your work, then unload and reload the studio.");
454 ladish_notify_simple(
455 LADISH_NOTIFY_URGENCY_HIGH
,
457 "JACK stopped unexpectedly.\n\n"
458 "Save your work, then unload and reload the studio.");
459 ladish_studio_handle_unexpected_jack_server_stop();
463 if (ladish_environment_consume_change(&g_studio
.env_store
, ladish_environment_jack_server_present
, &state
))
465 if (g_studio
.jack_graph_proxy
!= NULL
)
467 ladish_cqueue_clear(&g_studio
.cmd_queue
);
469 /* jack was started, this probably means that jackdbus has crashed */
470 log_error("JACK disappeared unexpectedly. Maybe it crashed.");
471 log_error("Save your work, then unload and reload the studio.");
472 ladish_notify_simple(
473 LADISH_NOTIFY_URGENCY_HIGH
,
475 "JACK disappeared unexpectedly. Maybe it crashed.\n\n"
476 "Save your work, then unload and reload the studio.");
477 ladish_environment_reset_stealth(&g_studio
.env_store
, ladish_environment_jack_server_started
);
479 ladish_studio_iterate_virtual_graphs(NULL
, ladish_studio_hide_vgraph_non_virtual
);
481 ladish_studio_handle_unexpected_jack_server_stop();
485 ladish_environment_assert_consumed(&g_studio
.env_store
);
488 static void ladish_studio_on_jack_server_started(void)
490 log_info("JACK server start detected.");
491 ladish_environment_set(&g_studio
.env_store
, ladish_environment_jack_server_started
);
494 static void ladish_studio_on_jack_server_stopped(void)
496 log_info("JACK server stop detected.");
497 ladish_environment_reset(&g_studio
.env_store
, ladish_environment_jack_server_started
);
500 static void ladish_studio_on_jack_server_appeared(void)
502 log_info("JACK controller appeared.");
503 ladish_environment_set(&g_studio
.env_store
, ladish_environment_jack_server_present
);
506 static void ladish_studio_on_jack_server_disappeared(void)
508 log_info("JACK controller disappeared.");
509 ladish_environment_reset(&g_studio
.env_store
, ladish_environment_jack_server_present
);
512 void ladish_on_app_renamed(void * context
, const char * old_name
, const char * new_app_name
)
514 ladish_client_handle client
;
516 client
= ladish_graph_find_client_by_name(g_studio
.jack_graph
, old_name
, false);
519 ladish_graph_rename_client(g_studio
.jack_graph
, client
, new_app_name
);
522 client
= ladish_graph_find_client_by_name(context
, old_name
, false);
525 ladish_graph_rename_client(context
, client
, new_app_name
);
529 bool ladish_studio_init(void)
531 log_info("studio object construct");
533 g_studios_dir
= catdup(g_base_dir
, STUDIOS_DIR
);
534 if (g_studios_dir
== NULL
)
536 log_error("catdup failed for '%s' and '%s'", g_base_dir
, STUDIOS_DIR
);
540 if (!ensure_dir_exist(g_studios_dir
, 0700))
542 goto free_studios_dir
;
545 INIT_LIST_HEAD(&g_studio
.all_connections
);
546 INIT_LIST_HEAD(&g_studio
.all_ports
);
547 INIT_LIST_HEAD(&g_studio
.all_clients
);
548 INIT_LIST_HEAD(&g_studio
.jack_connections
);
549 INIT_LIST_HEAD(&g_studio
.jack_ports
);
550 INIT_LIST_HEAD(&g_studio
.jack_clients
);
551 INIT_LIST_HEAD(&g_studio
.rooms
);
552 INIT_LIST_HEAD(&g_studio
.clients
);
553 INIT_LIST_HEAD(&g_studio
.ports
);
555 INIT_LIST_HEAD(&g_studio
.jack_conf
);
556 INIT_LIST_HEAD(&g_studio
.jack_params
);
558 g_studio
.dbus_object
= NULL
;
559 g_studio
.announced
= false;
560 g_studio
.name
= NULL
;
561 g_studio
.filename
= NULL
;
563 g_studio
.room_count
= 0;
565 if (!ladish_graph_create(&g_studio
.jack_graph
, NULL
))
567 log_error("ladish_graph_create() failed to create jack graph object.");
568 goto free_studios_dir
;
571 if (!ladish_graph_create(&g_studio
.studio_graph
, STUDIO_OBJECT_PATH
))
573 log_error("ladish_graph_create() failed to create studio graph object.");
574 goto jack_graph_destroy
;
577 if (!ladish_app_supervisor_create(&g_studio
.app_supervisor
, STUDIO_OBJECT_PATH
, "studio", g_studio
.studio_graph
, ladish_on_app_renamed
))
579 log_error("ladish_app_supervisor_create() failed.");
580 goto studio_graph_destroy
;
583 ladish_cqueue_init(&g_studio
.cmd_queue
);
584 ladish_environment_init(&g_studio
.env_store
);
586 if (!jack_proxy_init(
587 ladish_studio_on_jack_server_started
,
588 ladish_studio_on_jack_server_stopped
,
589 ladish_studio_on_jack_server_appeared
,
590 ladish_studio_on_jack_server_disappeared
))
592 log_error("jack_proxy_init() failed.");
593 goto app_supervisor_destroy
;
598 app_supervisor_destroy
:
599 ladish_app_supervisor_destroy(g_studio
.app_supervisor
);
600 studio_graph_destroy
:
601 ladish_graph_destroy(g_studio
.studio_graph
);
603 ladish_graph_destroy(g_studio
.jack_graph
);
610 void ladish_studio_uninit(void)
612 log_info("studio_uninit()");
616 ladish_cqueue_clear(&g_studio
.cmd_queue
);
618 ladish_graph_destroy(g_studio
.studio_graph
);
619 ladish_graph_destroy(g_studio
.jack_graph
);
623 log_info("studio object destroy");
626 struct on_child_exit_context
632 #define child_exit_context_ptr ((struct on_child_exit_context *)context)
636 ladish_studio_on_child_exit_callback(
638 ladish_graph_handle graph
,
639 ladish_app_supervisor_handle app_supervisor
)
641 child_exit_context_ptr
->found
= ladish_app_supervisor_child_exit(app_supervisor
, child_exit_context_ptr
->pid
);
642 /* if child is found, return false - it will cause iteration to stop */
643 /* if child is not found, return true - it will cause next supervisor to be checked */
644 return !child_exit_context_ptr
->found
;
647 #undef child_exit_context_ptr
649 void ladish_studio_on_child_exit(pid_t pid
)
651 struct on_child_exit_context context
;
654 context
.found
= false;
656 ladish_studio_iterate_virtual_graphs(&context
, ladish_studio_on_child_exit_callback
);
660 log_error("unknown child exit detected. pid is %llu", (unsigned long long)pid
);
664 bool ladish_studio_is_loaded(void)
666 return g_studio
.dbus_object
!= NULL
&& g_studio
.announced
;
669 bool ladish_studio_is_started(void)
671 return ladish_environment_get(&g_studio
.env_store
, ladish_environment_jack_server_started
);
674 bool ladish_studio_compose_filename(const char * name
, char ** filename_ptr_ptr
, char ** backup_filename_ptr_ptr
)
680 char * backup_filename_ptr
= NULL
;
682 len_dir
= strlen(g_studios_dir
);
684 filename_ptr
= malloc(len_dir
+ 1 + strlen(name
) * 3 + 4 + 1);
685 if (filename_ptr
== NULL
)
687 log_error("malloc failed to allocate memory for studio file path");
691 if (backup_filename_ptr_ptr
!= NULL
)
693 backup_filename_ptr
= malloc(len_dir
+ 1 + strlen(name
) * 3 + 4 + 4 + 1);
694 if (backup_filename_ptr
== NULL
)
696 log_error("malloc failed to allocate memory for studio backup file path");
703 memcpy(p
, g_studios_dir
, len_dir
);
712 *filename_ptr_ptr
= filename_ptr
;
714 if (backup_filename_ptr_ptr
!= NULL
)
716 strcpy(backup_filename_ptr
, filename_ptr
);
717 strcat(backup_filename_ptr
, ".bak");
718 *backup_filename_ptr_ptr
= backup_filename_ptr
;
724 struct ladish_cqueue
* ladish_studio_get_cmd_queue(void)
726 return &g_studio
.cmd_queue
;
729 ladish_virtualizer_handle
ladish_studio_get_virtualizer(void)
731 return g_studio
.virtualizer
;
734 ladish_graph_handle
ladish_studio_get_jack_graph(void)
736 return g_studio
.jack_graph
;
739 ladish_graph_handle
ladish_studio_get_studio_graph(void)
741 return g_studio
.studio_graph
;
744 ladish_app_supervisor_handle
ladish_studio_get_studio_app_supervisor(void)
746 return g_studio
.app_supervisor
;
749 struct ladish_studio_app_supervisor_match_context
752 ladish_app_supervisor_handle supervisor
;
755 #define iterate_context_ptr ((struct ladish_studio_app_supervisor_match_context *)context)
757 static bool ladish_studio_app_supervisor_match(void * context
, ladish_graph_handle graph
, ladish_app_supervisor_handle app_supervisor
)
759 ASSERT(strcmp(ladish_app_supervisor_get_opath(app_supervisor
), ladish_graph_get_opath(graph
)) == 0);
760 if (strcmp(ladish_app_supervisor_get_opath(app_supervisor
), iterate_context_ptr
->opath
) == 0)
762 ASSERT(iterate_context_ptr
->supervisor
== NULL
);
763 iterate_context_ptr
->supervisor
= app_supervisor
;
764 return false; /* stop iteration */
767 return true; /* continue iteration */
770 #undef iterate_context_ptr
772 ladish_app_supervisor_handle
ladish_studio_find_app_supervisor(const char * opath
)
774 struct ladish_studio_app_supervisor_match_context ctx
;
777 ctx
.supervisor
= NULL
;
778 ladish_studio_iterate_virtual_graphs(&ctx
, ladish_studio_app_supervisor_match
);
779 return ctx
.supervisor
;
782 bool ladish_studios_iterate(void * call_ptr
, void * context
, bool (* callback
)(void * call_ptr
, void * context
, const char * studio
, uint32_t modtime
))
785 struct dirent
* dentry
;
791 dir
= opendir(g_studios_dir
);
794 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "Cannot open directory '%s': %d (%s)", g_studios_dir
, errno
, strerror(errno
));
798 while ((dentry
= readdir(dir
)) != NULL
)
800 len
= strlen(dentry
->d_name
);
801 if (len
<= 4 || strcmp(dentry
->d_name
+ (len
- 4), ".xml") != 0)
804 path
= catdup(g_studios_dir
, dentry
->d_name
);
807 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "catdup() failed");
811 if (stat(path
, &st
) != 0)
813 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "failed to stat '%s': %d (%s)", path
, errno
, strerror(errno
));
820 if (!S_ISREG(st
.st_mode
))
822 //log_info("Ignoring direntry that is not regular file. Mode is %07o", st.st_mode);
826 name
= malloc(len
- 4 + 1);
829 log_error("malloc() failed.");
834 name
[unescape(dentry
->d_name
, len
- 4, name
)] = 0;
835 //log_info("name = '%s'", name);
837 if (!callback(call_ptr
, context
, name
, st
.st_mtime
))
851 bool ladish_studio_delete(void * call_ptr
, const char * studio_name
)
860 if (!ladish_studio_compose_filename(studio_name
, &filename
, &bak_filename
))
862 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "failed to compose studio filename");
866 log_info("Deleting studio ('%s')", filename
);
868 if (unlink(filename
) != 0)
870 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "unlink(%s) failed: %d (%s)", filename
, errno
, strerror(errno
));
874 /* try to delete the backup file */
875 if (stat(bak_filename
, &st
) == 0)
877 if (unlink(bak_filename
) != 0)
879 /* failing to delete backup file will not case delete command failure */
880 log_error("unlink(%s) failed: %d (%s)", bak_filename
, errno
, strerror(errno
));
894 ladish_studio_iterate_virtual_graphs(
898 ladish_graph_handle graph
,
899 ladish_app_supervisor_handle app_supervisor
))
901 struct list_head
* node_ptr
;
902 ladish_room_handle room
;
903 ladish_app_supervisor_handle room_app_supervisor
;
904 ladish_graph_handle room_graph
;
906 if (!callback(context
, g_studio
.studio_graph
, g_studio
.app_supervisor
))
911 list_for_each(node_ptr
, &g_studio
.rooms
)
913 room
= ladish_room_from_list_node(node_ptr
);
914 room_app_supervisor
= ladish_room_get_app_supervisor(room
);
915 ASSERT(room_app_supervisor
!= NULL
);
916 room_graph
= ladish_room_get_graph(room
);
918 if (!callback(context
, room_graph
, room_app_supervisor
))
928 ladish_studio_iterate_rooms(
932 ladish_room_handle room
))
934 struct list_head
* node_ptr
;
935 ladish_room_handle room
;
937 list_for_each(node_ptr
, &g_studio
.rooms
)
939 room
= ladish_room_from_list_node(node_ptr
);
940 if (!callback(context
, room
))
949 bool ladish_studio_has_rooms(void)
951 return !list_empty(&g_studio
.rooms
);
954 static bool ladish_studio_stop_app_supervisor(void * context
, ladish_graph_handle graph
, ladish_app_supervisor_handle app_supervisor
)
956 ladish_app_supervisor_stop(app_supervisor
);
957 return true; /* iterate all supervisors */
960 void ladish_studio_stop_app_supervisors(void)
962 ladish_studio_iterate_virtual_graphs(NULL
, ladish_studio_stop_app_supervisor
);
965 void ladish_studio_emit_renamed(void)
967 dbus_signal_emit(g_dbus_connection
, STUDIO_OBJECT_PATH
, IFACE_STUDIO
, "StudioRenamed", "s", &g_studio
.name
);
970 unsigned int ladish_studio_get_room_index(void)
972 return ++g_studio
.room_count
;
975 void ladish_studio_release_room_index(unsigned int index
)
977 if (index
== g_studio
.room_count
)
979 g_studio
.room_count
--;
983 void ladish_studio_remove_all_rooms(void)
985 while (!list_empty(&g_studio
.rooms
))
987 ladish_room_destroy(ladish_room_from_list_node(g_studio
.rooms
.next
));
991 ladish_room_handle
ladish_studio_find_room_by_uuid(const uuid_t room_uuid_ptr
)
993 struct list_head
* node_ptr
;
994 ladish_room_handle room
;
997 list_for_each(node_ptr
, &g_studio
.rooms
)
999 room
= ladish_room_from_list_node(node_ptr
);
1000 ladish_room_get_uuid(room
, room_uuid
);
1001 if (uuid_compare(room_uuid
, room_uuid_ptr
) == 0)
1010 /**********************************************************************************/
1012 /**********************************************************************************/
1014 static void ladish_studio_dbus_get_name(struct dbus_method_call
* call_ptr
)
1016 method_return_new_single(call_ptr
, DBUS_TYPE_STRING
, &g_studio
.name
);
1019 static void ladish_studio_dbus_rename(struct dbus_method_call
* call_ptr
)
1021 const char * new_name
;
1022 char * new_name_dup
;
1024 if (!dbus_message_get_args(call_ptr
->message
, &g_dbus_error
, DBUS_TYPE_STRING
, &new_name
, DBUS_TYPE_INVALID
))
1026 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
1027 dbus_error_free(&g_dbus_error
);
1031 log_info("Rename studio request (%s)", new_name
);
1033 new_name_dup
= strdup(new_name
);
1034 if (new_name_dup
== NULL
)
1036 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_GENERIC
, "strdup() failed to allocate new name.");
1040 free(g_studio
.name
);
1041 g_studio
.name
= new_name_dup
;
1043 method_return_new_void(call_ptr
);
1044 ladish_studio_emit_renamed();
1047 static void ladish_studio_dbus_save(struct dbus_method_call
* call_ptr
)
1049 log_info("Save studio request");
1051 if (ladish_command_save_studio(call_ptr
, &g_studio
.cmd_queue
, g_studio
.name
))
1053 method_return_new_void(call_ptr
);
1057 static void ladish_studio_dbus_save_as(struct dbus_method_call
* call_ptr
)
1059 const char * new_name
;
1061 log_info("SaveAs studio request");
1063 if (!dbus_message_get_args(call_ptr
->message
, &g_dbus_error
, DBUS_TYPE_STRING
, &new_name
, DBUS_TYPE_INVALID
))
1065 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
1066 dbus_error_free(&g_dbus_error
);
1070 if (ladish_command_save_studio(call_ptr
, &g_studio
.cmd_queue
, new_name
))
1072 method_return_new_void(call_ptr
);
1076 static void ladish_studio_dbus_unload(struct dbus_method_call
* call_ptr
)
1078 log_info("Unload studio request");
1080 if (ladish_command_unload_studio(call_ptr
, &g_studio
.cmd_queue
))
1082 method_return_new_void(call_ptr
);
1086 static void ladish_studio_dbus_stop(struct dbus_method_call
* call_ptr
)
1088 log_info("Stop studio request");
1090 g_studio
.automatic
= false; /* even if it was automatic, it is not anymore because user knows about it */
1092 if (ladish_command_stop_studio(call_ptr
, &g_studio
.cmd_queue
))
1094 method_return_new_void(call_ptr
);
1098 static void ladish_studio_dbus_start(struct dbus_method_call
* call_ptr
)
1100 log_info("Start studio request");
1102 g_studio
.automatic
= false; /* even if it was automatic, it is not anymore because user knows about it */
1104 if (ladish_command_start_studio(call_ptr
, &g_studio
.cmd_queue
))
1106 method_return_new_void(call_ptr
);
1110 static void ladish_studio_dbus_is_started(struct dbus_method_call
* call_ptr
)
1112 dbus_bool_t started
;
1114 started
= g_studio
.jack_graph_proxy
!= NULL
;
1116 method_return_new_single(call_ptr
, DBUS_TYPE_BOOLEAN
, &started
);
1119 static void ladish_studio_dbus_create_room(struct dbus_method_call
* call_ptr
)
1121 const char * room_name
;
1122 const char * template_name
;
1124 dbus_error_init(&g_dbus_error
);
1126 if (!dbus_message_get_args(call_ptr
->message
, &g_dbus_error
, DBUS_TYPE_STRING
, &room_name
, DBUS_TYPE_STRING
, &template_name
, DBUS_TYPE_INVALID
))
1128 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
1129 dbus_error_free(&g_dbus_error
);
1133 if (ladish_command_create_room(call_ptr
, ladish_studio_get_cmd_queue(), room_name
, template_name
))
1135 method_return_new_void(call_ptr
);
1139 static void ladish_studio_dbus_get_room_list(struct dbus_method_call
* call_ptr
)
1141 DBusMessageIter iter
, array_iter
;
1142 DBusMessageIter struct_iter
;
1143 struct list_head
* node_ptr
;
1144 ladish_room_handle room
;
1146 call_ptr
->reply
= dbus_message_new_method_return(call_ptr
->message
);
1147 if (call_ptr
->reply
== NULL
)
1152 dbus_message_iter_init_append(call_ptr
->reply
, &iter
);
1154 if (!dbus_message_iter_open_container(&iter
, DBUS_TYPE_ARRAY
, "(sa{sv})", &array_iter
))
1159 list_for_each(node_ptr
, &g_studio
.rooms
)
1161 room
= ladish_room_from_list_node(node_ptr
);
1163 if (!dbus_message_iter_open_container(&array_iter
, DBUS_TYPE_STRUCT
, NULL
, &struct_iter
))
1166 if (!ladish_studio_fill_room_info(&struct_iter
, room
))
1169 if (!dbus_message_iter_close_container(&array_iter
, &struct_iter
))
1173 if (!dbus_message_iter_close_container(&iter
, &array_iter
))
1181 dbus_message_unref(call_ptr
->reply
);
1182 call_ptr
->reply
= NULL
;
1185 log_error("Ran out of memory trying to construct method return");
1188 static void ladish_studio_dbus_delete_room(struct dbus_method_call
* call_ptr
)
1192 dbus_error_init(&g_dbus_error
);
1194 if (!dbus_message_get_args(call_ptr
->message
, &g_dbus_error
, DBUS_TYPE_STRING
, &name
, DBUS_TYPE_INVALID
))
1196 lash_dbus_error(call_ptr
, LASH_DBUS_ERROR_INVALID_ARGS
, "Invalid arguments to method \"%s\": %s", call_ptr
->method_name
, g_dbus_error
.message
);
1197 dbus_error_free(&g_dbus_error
);
1201 if (ladish_command_delete_room(call_ptr
, ladish_studio_get_cmd_queue(), name
))
1203 method_return_new_void(call_ptr
);
1207 METHOD_ARGS_BEGIN(GetName
, "Get studio name")
1208 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
1211 METHOD_ARGS_BEGIN(Rename
, "Rename studio")
1212 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1215 METHOD_ARGS_BEGIN(Save
, "Save studio")
1218 METHOD_ARGS_BEGIN(SaveAs
, "SaveAs studio")
1219 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1222 METHOD_ARGS_BEGIN(Unload
, "Unload studio")
1225 METHOD_ARGS_BEGIN(Start
, "Start studio")
1228 METHOD_ARGS_BEGIN(Stop
, "Stop studio")
1231 METHOD_ARGS_BEGIN(IsStarted
, "Check whether studio is started")
1232 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
1235 METHOD_ARGS_BEGIN(CreateRoom
, "Create new studio room")
1236 METHOD_ARG_DESCRIBE_IN("room_name", "s", "Studio room name")
1237 METHOD_ARG_DESCRIBE_IN("room_template_name", "s", "Room template name")
1240 METHOD_ARGS_BEGIN(GetRoomList
, "Get list of rooms in this studio")
1241 METHOD_ARG_DESCRIBE_OUT("room_list", "a(sa{sv})", "List of studio rooms: opaths and properties")
1244 METHOD_ARGS_BEGIN(DeleteRoom
, "Delete studio room")
1245 METHOD_ARG_DESCRIBE_IN("room_name", "s", "Name of studio room to delete")
1249 METHOD_DESCRIBE(GetName
, ladish_studio_dbus_get_name
) /* sync */
1250 METHOD_DESCRIBE(Rename
, ladish_studio_dbus_rename
) /* sync */
1251 METHOD_DESCRIBE(Save
, ladish_studio_dbus_save
) /* async */
1252 METHOD_DESCRIBE(SaveAs
, ladish_studio_dbus_save_as
) /* async */
1253 METHOD_DESCRIBE(Unload
, ladish_studio_dbus_unload
) /* async */
1254 METHOD_DESCRIBE(Start
, ladish_studio_dbus_start
) /* async */
1255 METHOD_DESCRIBE(Stop
, ladish_studio_dbus_stop
) /* async */
1256 METHOD_DESCRIBE(IsStarted
, ladish_studio_dbus_is_started
) /* sync */
1257 METHOD_DESCRIBE(CreateRoom
, ladish_studio_dbus_create_room
) /* async */
1258 METHOD_DESCRIBE(GetRoomList
, ladish_studio_dbus_get_room_list
) /* sync */
1259 METHOD_DESCRIBE(DeleteRoom
, ladish_studio_dbus_delete_room
) /* async */
1262 SIGNAL_ARGS_BEGIN(StudioRenamed
, "Studio name changed")
1263 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
1266 SIGNAL_ARGS_BEGIN(StudioStarted
, "Studio started")
1269 SIGNAL_ARGS_BEGIN(StudioCrashed
, "Studio crashed")
1272 SIGNAL_ARGS_BEGIN(StudioStopped
, "Studio stopped")
1275 SIGNAL_ARGS_BEGIN(RoomAppeared
, "Room D-Bus object appeared")
1276 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1277 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1280 SIGNAL_ARGS_BEGIN(RoomChanged
, "Room D-Bus object changed")
1281 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1282 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1285 SIGNAL_ARGS_BEGIN(RoomDisappeared
, "Room D-Bus object disappeared")
1286 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1287 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room properties")
1291 SIGNAL_DESCRIBE(StudioRenamed
)
1292 SIGNAL_DESCRIBE(StudioStarted
)
1293 SIGNAL_DESCRIBE(StudioCrashed
)
1294 SIGNAL_DESCRIBE(StudioStopped
)
1295 SIGNAL_DESCRIBE(RoomAppeared
)
1296 SIGNAL_DESCRIBE(RoomDisappeared
)
1297 SIGNAL_DESCRIBE(RoomChanged
)
1300 INTERFACE_BEGIN(g_interface_studio
, IFACE_STUDIO
)
1301 INTERFACE_DEFAULT_HANDLER
1302 INTERFACE_EXPOSE_METHODS
1303 INTERFACE_EXPOSE_SIGNALS