daemon: coherent naming of studio related functions
[ladish.git] / daemon / studio.c
blob46b0a2a9e99c1f04b1e0592cfd29d6b36e9e2650
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
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.
28 #include "common.h"
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <dirent.h>
35 #include "studio_internal.h"
36 #include "../dbus_constants.h"
37 #include "control.h"
38 #include "../catdup.h"
39 #include "dirhelpers.h"
40 #include "graph_dict.h"
41 #include "escape.h"
42 #include "studio.h"
43 #include "../proxies/jmcore_proxy.h"
44 #include "../proxies/notify_proxy.h"
46 #define STUDIOS_DIR "/studios/"
47 char * g_studios_dir;
49 struct studio g_studio;
51 bool ladish_studio_name_generate(char ** name_ptr)
53 time_t now;
54 char timestamp_str[26];
55 char * name;
57 time(&now);
58 //ctime_r(&now, timestamp_str);
59 //timestamp_str[24] = 0;
60 snprintf(timestamp_str, sizeof(timestamp_str), "%llu", (unsigned long long)now);
62 name = catdup("Studio ", timestamp_str);
63 if (name == NULL)
65 log_error("catdup failed to create studio name");
66 return false;
69 *name_ptr = name;
70 return true;
73 bool ladish_studio_publish(void)
75 dbus_object_path object;
77 ASSERT(g_studio.name != NULL);
79 object = dbus_object_path_new(
80 STUDIO_OBJECT_PATH,
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,
85 NULL);
86 if (object == NULL)
88 log_error("dbus_object_path_new() failed");
89 return false;
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);
96 return false;
99 log_info("Studio D-Bus object created. \"%s\"", g_studio.name);
101 g_studio.dbus_object = object;
103 emit_studio_appeared();
104 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio loaded", NULL);
106 return true;
109 void ladish_studio_emit_started(void)
111 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStarted", "");
114 void ladish_studio_emit_crashed(void)
116 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioCrashed", "");
119 void ladish_studio_emit_stopped(void)
121 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioStopped", "");
124 static bool ladish_studio_fill_room_info(DBusMessageIter * iter_ptr, ladish_room_handle room)
126 DBusMessageIter dict_iter;
127 const char * name;
128 uuid_t template_uuid;
129 ladish_room_handle template;
130 const char * template_name;
131 const char * opath;
133 name = ladish_room_get_name(room);
134 opath = ladish_room_get_opath(room);
136 if (!ladish_room_get_template_uuid(room, template_uuid))
138 template = NULL;
139 template_name = NULL;
141 else
143 template = find_room_template_by_uuid(template_uuid);
144 if (template != NULL)
146 template_name = ladish_room_get_name(template);
148 else
150 template_name = NULL;
154 if (!dbus_message_iter_append_basic(iter_ptr, DBUS_TYPE_STRING, &opath))
156 log_error("dbus_message_iter_append_basic() failed.");
157 return false;
160 if (!dbus_message_iter_open_container(iter_ptr, DBUS_TYPE_ARRAY, "{sv}", &dict_iter))
162 log_error("dbus_message_iter_open_container() failed.");
163 return false;
166 if (!dbus_maybe_add_dict_entry_string(&dict_iter, "template", template_name))
168 log_error("dbus_maybe_add_dict_entry_string() failed.");
169 return false;
172 if (!dbus_maybe_add_dict_entry_string(&dict_iter, "name", name))
174 log_error("dbus_maybe_add_dict_entry_string() failed.");
175 return false;
178 if (!dbus_message_iter_close_container(iter_ptr, &dict_iter))
180 log_error("dbus_message_iter_close_container() failed.");
181 return false;
184 return true;
187 static void ladish_studio_emit_room_appeared(ladish_room_handle room)
189 DBusMessage * message_ptr;
190 DBusMessageIter iter;
192 message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomAppeared");
193 if (message_ptr == NULL)
195 log_error("dbus_message_new_signal() failed.");
196 return;
199 dbus_message_iter_init_append(message_ptr, &iter);
201 if (ladish_studio_fill_room_info(&iter, room))
203 dbus_signal_send(g_dbus_connection, message_ptr);
206 dbus_message_unref(message_ptr);
209 static void ladish_studio_emit_room_disappeared(ladish_room_handle room)
211 DBusMessage * message_ptr;
212 DBusMessageIter iter;
214 message_ptr = dbus_message_new_signal(STUDIO_OBJECT_PATH, IFACE_STUDIO, "RoomDisappeared");
215 if (message_ptr == NULL)
217 log_error("dbus_message_new_signal() failed.");
218 return;
221 dbus_message_iter_init_append(message_ptr, &iter);
223 if (ladish_studio_fill_room_info(&iter, room))
225 dbus_signal_send(g_dbus_connection, message_ptr);
228 dbus_message_unref(message_ptr);
231 bool
232 ladish_studio_set_graph_connection_handlers(
233 void * context,
234 ladish_graph_handle graph,
235 ladish_app_supervisor_handle app_supervisor)
237 ladish_virtualizer_set_graph_connection_handlers(context, graph);
238 return true; /* iterate all graphs */
241 void ladish_studio_on_event_jack_started(void)
243 if (!ladish_studio_fetch_jack_settings())
245 log_error("studio_fetch_jack_settings() failed.");
247 return;
250 log_info("jack conf successfully retrieved");
251 g_studio.jack_conf_valid = true;
253 if (!graph_proxy_create(JACKDBUS_SERVICE_NAME, JACKDBUS_OBJECT_PATH, false, &g_studio.jack_graph_proxy))
255 log_error("graph_proxy_create() failed for jackdbus");
257 else
259 if (!ladish_virtualizer_create(g_studio.jack_graph_proxy, g_studio.jack_graph, &g_studio.virtualizer))
261 log_error("ladish_virtualizer_create() failed.");
263 else
265 ladish_studio_iterate_virtual_graphs(g_studio.virtualizer, ladish_studio_set_graph_connection_handlers);
268 if (!graph_proxy_activate(g_studio.jack_graph_proxy))
270 log_error("graph_proxy_activate() failed.");
274 ladish_app_supervisor_autorun(g_studio.app_supervisor);
276 ladish_studio_emit_started();
277 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio started", NULL);
280 static void ladish_studio_on_jack_stopped_internal(void)
282 if (g_studio.virtualizer)
284 ladish_virtualizer_destroy(g_studio.virtualizer);
285 g_studio.virtualizer = NULL;
288 if (g_studio.jack_graph_proxy)
290 graph_proxy_destroy(g_studio.jack_graph_proxy);
291 g_studio.jack_graph_proxy = NULL;
295 void ladish_studio_on_event_jack_stopped(void)
297 ladish_studio_emit_stopped();
298 ladish_studio_on_jack_stopped_internal();
299 ladish_notify_simple(LADISH_NOTIFY_URGENCY_NORMAL, "Studio stopped", NULL);
302 void ladish_studio_handle_unexpected_jack_server_stop(void)
304 ladish_studio_emit_crashed();
305 ladish_studio_on_jack_stopped_internal();
307 /* TODO: if user wants, restart jack server and reconnect all jack apps to it */
310 static bool ladish_studio_hide_vgraph_non_virtual(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
312 ladish_graph_hide_non_virtual(graph);
313 return true; /* iterate all vgraphs */
316 void ladish_studio_run(void)
318 bool state;
320 ladish_cqueue_run(&g_studio.cmd_queue);
321 if (g_quit)
322 { /* if quit is requested, don't bother to process external events */
323 return;
326 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_started, &state))
328 ladish_cqueue_clear(&g_studio.cmd_queue);
330 if (state)
332 ladish_environment_ignore(&g_studio.env_store, ladish_environment_jack_server_present);
334 /* Automatic studio creation on JACK server start */
335 if (g_studio.dbus_object == NULL)
337 ASSERT(g_studio.name == NULL);
338 if (!ladish_studio_name_generate(&g_studio.name))
340 log_error("studio_name_generate() failed.");
341 return;
344 g_studio.automatic = true;
346 ladish_studio_publish();
349 ladish_studio_on_event_jack_started();
351 else
353 /* JACK stopped but this was not expected. When expected.
354 * the change will be consumed by the run method of the studio stop command */
356 if (g_studio.automatic)
358 log_info("Unloading automatic studio.");
359 ladish_command_unload_studio(NULL, &g_studio.cmd_queue);
361 ladish_studio_on_event_jack_stopped();
362 return;
365 log_error("JACK stopped unexpectedly.");
366 log_error("Save your work, then unload and reload the studio.");
367 ladish_notify_simple(
368 LADISH_NOTIFY_URGENCY_HIGH,
369 "Studio crashed",
370 "JACK stopped unexpectedly.\n\n"
371 "Save your work, then unload and reload the studio.");
372 ladish_studio_handle_unexpected_jack_server_stop();
376 if (ladish_environment_consume_change(&g_studio.env_store, ladish_environment_jack_server_present, &state))
378 if (g_studio.jack_graph_proxy != NULL)
380 ladish_cqueue_clear(&g_studio.cmd_queue);
382 /* jack was started, this probably means that jackdbus has crashed */
383 log_error("JACK disappeared unexpectedly. Maybe it crashed.");
384 log_error("Save your work, then unload and reload the studio.");
385 ladish_notify_simple(
386 LADISH_NOTIFY_URGENCY_HIGH,
387 "Studio crashed",
388 "JACK disappeared unexpectedly. Maybe it crashed.\n\n"
389 "Save your work, then unload and reload the studio.");
390 ladish_environment_reset_stealth(&g_studio.env_store, ladish_environment_jack_server_started);
392 ladish_studio_iterate_virtual_graphs(NULL, ladish_studio_hide_vgraph_non_virtual);
394 ladish_studio_handle_unexpected_jack_server_stop();
398 ladish_environment_assert_consumed(&g_studio.env_store);
401 static void ladish_studio_on_jack_server_started(void)
403 log_info("JACK server start detected.");
404 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_started);
407 static void ladish_studio_on_jack_server_stopped(void)
409 log_info("JACK server stop detected.");
410 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_started);
413 static void ladish_studio_on_jack_server_appeared(void)
415 log_info("JACK controller appeared.");
416 ladish_environment_set(&g_studio.env_store, ladish_environment_jack_server_present);
419 static void ladish_studio_on_jack_server_disappeared(void)
421 log_info("JACK controller disappeared.");
422 ladish_environment_reset(&g_studio.env_store, ladish_environment_jack_server_present);
425 void ladish_on_app_renamed(void * context, const char * old_name, const char * new_app_name)
427 ladish_client_handle client;
429 client = ladish_graph_find_client_by_name(g_studio.jack_graph, old_name);
430 if (client != NULL)
432 ladish_graph_rename_client(g_studio.jack_graph, client, new_app_name);
435 client = ladish_graph_find_client_by_name(context, old_name);
436 if (client != NULL)
438 ladish_graph_rename_client(context, client, new_app_name);
442 bool ladish_studio_init(void)
444 log_info("studio object construct");
446 g_studios_dir = catdup(g_base_dir, STUDIOS_DIR);
447 if (g_studios_dir == NULL)
449 log_error("catdup failed for '%s' and '%s'", g_base_dir, STUDIOS_DIR);
450 goto fail;
453 if (!ensure_dir_exist(g_studios_dir, 0700))
455 goto free_studios_dir;
458 INIT_LIST_HEAD(&g_studio.all_connections);
459 INIT_LIST_HEAD(&g_studio.all_ports);
460 INIT_LIST_HEAD(&g_studio.all_clients);
461 INIT_LIST_HEAD(&g_studio.jack_connections);
462 INIT_LIST_HEAD(&g_studio.jack_ports);
463 INIT_LIST_HEAD(&g_studio.jack_clients);
464 INIT_LIST_HEAD(&g_studio.rooms);
465 INIT_LIST_HEAD(&g_studio.clients);
466 INIT_LIST_HEAD(&g_studio.ports);
468 INIT_LIST_HEAD(&g_studio.jack_conf);
469 INIT_LIST_HEAD(&g_studio.jack_params);
471 g_studio.dbus_object = NULL;
472 g_studio.name = NULL;
473 g_studio.filename = NULL;
475 g_studio.room_count = 0;
477 if (!ladish_graph_create(&g_studio.jack_graph, NULL))
479 log_error("ladish_graph_create() failed to create jack graph object.");
480 goto free_studios_dir;
483 if (!ladish_graph_create(&g_studio.studio_graph, STUDIO_OBJECT_PATH))
485 log_error("ladish_graph_create() failed to create studio graph object.");
486 goto jack_graph_destroy;
489 if (!ladish_app_supervisor_create(&g_studio.app_supervisor, STUDIO_OBJECT_PATH, "studio", g_studio.studio_graph, ladish_on_app_renamed))
491 log_error("ladish_app_supervisor_create() failed.");
492 goto studio_graph_destroy;
495 ladish_cqueue_init(&g_studio.cmd_queue);
496 ladish_environment_init(&g_studio.env_store);
498 if (!jack_proxy_init(
499 ladish_studio_on_jack_server_started,
500 ladish_studio_on_jack_server_stopped,
501 ladish_studio_on_jack_server_appeared,
502 ladish_studio_on_jack_server_disappeared))
504 log_error("jack_proxy_init() failed.");
505 goto app_supervisor_destroy;
508 return true;
510 app_supervisor_destroy:
511 ladish_app_supervisor_destroy(g_studio.app_supervisor);
512 studio_graph_destroy:
513 ladish_graph_destroy(g_studio.studio_graph);
514 jack_graph_destroy:
515 ladish_graph_destroy(g_studio.jack_graph);
516 free_studios_dir:
517 free(g_studios_dir);
518 fail:
519 return false;
522 void ladish_studio_uninit(void)
524 log_info("studio_uninit()");
526 jack_proxy_uninit();
528 ladish_cqueue_clear(&g_studio.cmd_queue);
530 ladish_graph_destroy(g_studio.studio_graph);
531 ladish_graph_destroy(g_studio.jack_graph);
533 free(g_studios_dir);
535 log_info("studio object destroy");
538 struct on_child_exit_context
540 pid_t pid;
541 bool found;
544 #define child_exit_context_ptr ((struct on_child_exit_context *)context)
546 static
547 bool
548 ladish_studio_on_child_exit_callback(
549 void * context,
550 ladish_graph_handle graph,
551 ladish_app_supervisor_handle app_supervisor)
553 child_exit_context_ptr->found = ladish_app_supervisor_child_exit(app_supervisor, child_exit_context_ptr->pid);
554 /* if child is found, return false - it will cause iteration to stop */
555 /* if child is not found, return true - it will cause next supervisor to be checked */
556 return !child_exit_context_ptr->found;
559 #undef child_exit_context_ptr
561 void ladish_studio_on_child_exit(pid_t pid)
563 struct on_child_exit_context context;
565 context.pid = pid;
566 context.found = false;
568 ladish_studio_iterate_virtual_graphs(&context, ladish_studio_on_child_exit_callback);
570 if (!context.found)
572 log_error("unknown child exit detected. pid is %llu", (unsigned long long)pid);
576 bool ladish_studio_is_loaded(void)
578 return g_studio.dbus_object != NULL;
581 bool ladish_studio_is_started(void)
583 return ladish_environment_get(&g_studio.env_store, ladish_environment_jack_server_started);
586 bool ladish_studio_compose_filename(const char * name, char ** filename_ptr_ptr, char ** backup_filename_ptr_ptr)
588 size_t len_dir;
589 char * p;
590 const char * src;
591 char * filename_ptr;
592 char * backup_filename_ptr = NULL;
594 len_dir = strlen(g_studios_dir);
596 filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 1);
597 if (filename_ptr == NULL)
599 log_error("malloc failed to allocate memory for studio file path");
600 return false;
603 if (backup_filename_ptr_ptr != NULL)
605 backup_filename_ptr = malloc(len_dir + 1 + strlen(name) * 3 + 4 + 4 + 1);
606 if (backup_filename_ptr == NULL)
608 log_error("malloc failed to allocate memory for studio backup file path");
609 free(filename_ptr);
610 return false;
614 p = filename_ptr;
615 memcpy(p, g_studios_dir, len_dir);
616 p += len_dir;
618 *p++ = '/';
620 src = name;
621 escape(&src, &p);
622 strcpy(p, ".xml");
624 *filename_ptr_ptr = filename_ptr;
626 if (backup_filename_ptr_ptr != NULL)
628 strcpy(backup_filename_ptr, filename_ptr);
629 strcat(backup_filename_ptr, ".bak");
630 *backup_filename_ptr_ptr = backup_filename_ptr;
633 return true;
636 struct ladish_cqueue * ladish_studio_get_cmd_queue(void)
638 return &g_studio.cmd_queue;
641 struct ladish_studio_app_supervisor_match_context
643 const char * opath;
644 ladish_app_supervisor_handle supervisor;
647 #define iterate_context_ptr ((struct ladish_studio_app_supervisor_match_context *)context)
649 static bool ladish_studio_app_supervisor_match(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
651 ASSERT(strcmp(ladish_app_supervisor_get_opath(app_supervisor), ladish_graph_get_opath(graph)) == 0);
652 if (strcmp(ladish_app_supervisor_get_opath(app_supervisor), iterate_context_ptr->opath) == 0)
654 ASSERT(iterate_context_ptr->supervisor == NULL);
655 iterate_context_ptr->supervisor = app_supervisor;
656 return false; /* stop iteration */
659 return true; /* continue iteration */
662 #undef iterate_context_ptr
664 ladish_app_supervisor_handle ladish_studio_find_app_supervisor(const char * opath)
666 struct ladish_studio_app_supervisor_match_context ctx;
668 ctx.opath = opath;
669 ctx.supervisor = NULL;
670 ladish_studio_iterate_virtual_graphs(&ctx, ladish_studio_app_supervisor_match);
671 return ctx.supervisor;
674 bool ladish_studios_iterate(void * call_ptr, void * context, bool (* callback)(void * call_ptr, void * context, const char * studio, uint32_t modtime))
676 DIR * dir;
677 struct dirent * dentry;
678 size_t len;
679 struct stat st;
680 char * path;
681 char * name;
683 dir = opendir(g_studios_dir);
684 if (dir == NULL)
686 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot open directory '%s': %d (%s)", g_studios_dir, errno, strerror(errno));
687 return false;
690 while ((dentry = readdir(dir)) != NULL)
692 len = strlen(dentry->d_name);
693 if (len <= 4 || strcmp(dentry->d_name + (len - 4), ".xml") != 0)
694 continue;
696 path = catdup(g_studios_dir, dentry->d_name);
697 if (path == NULL)
699 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "catdup() failed");
700 return false;
703 if (stat(path, &st) != 0)
705 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to stat '%s': %d (%s)", path, errno, strerror(errno));
706 free(path);
707 return false;
710 free(path);
712 if (!S_ISREG(st.st_mode))
714 //log_info("Ignoring direntry that is not regular file. Mode is %07o", st.st_mode);
715 continue;
718 name = malloc(len - 4 + 1);
719 if (name == NULL)
721 log_error("malloc() failed.");
722 closedir(dir);
723 return false;
726 name[unescape(dentry->d_name, len - 4, name)] = 0;
727 //log_info("name = '%s'", name);
729 if (!callback(call_ptr, context, name, st.st_mtime))
731 free(name);
732 closedir(dir);
733 return false;
736 free(name);
739 closedir(dir);
740 return true;
743 bool ladish_studio_delete(void * call_ptr, const char * studio_name)
745 char * filename;
746 char * bak_filename;
747 struct stat st;
748 bool ret;
750 ret = false;
752 if (!ladish_studio_compose_filename(studio_name, &filename, &bak_filename))
754 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "failed to compose studio filename");
755 goto exit;
758 log_info("Deleting studio ('%s')", filename);
760 if (unlink(filename) != 0)
762 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "unlink(%s) failed: %d (%s)", filename, errno, strerror(errno));
763 goto free;
766 /* try to delete the backup file */
767 if (stat(bak_filename, &st) == 0)
769 if (unlink(bak_filename) != 0)
771 /* failing to delete backup file will not case delete command failure */
772 log_error("unlink(%s) failed: %d (%s)", bak_filename, errno, strerror(errno));
776 ret = true;
778 free:
779 free(filename);
780 free(bak_filename);
781 exit:
782 return ret;
785 bool
786 ladish_studio_iterate_virtual_graphs(
787 void * context,
788 bool (* callback)(
789 void * context,
790 ladish_graph_handle graph,
791 ladish_app_supervisor_handle app_supervisor))
793 struct list_head * node_ptr;
794 ladish_room_handle room;
795 ladish_app_supervisor_handle room_app_supervisor;
796 ladish_graph_handle room_graph;
798 if (!callback(context, g_studio.studio_graph, g_studio.app_supervisor))
800 return false;
803 list_for_each(node_ptr, &g_studio.rooms)
805 room = ladish_room_from_list_node(node_ptr);
806 room_app_supervisor = ladish_room_get_app_supervisor(room);
807 ASSERT(room_app_supervisor != NULL);
808 room_graph = ladish_room_get_graph(room);
810 if (!callback(context, room_graph, room_app_supervisor))
812 return false;
816 return true;
819 static bool ladish_studio_stop_app_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
821 ladish_app_supervisor_stop(app_supervisor);
822 return true; /* iterate all supervisors */
825 void ladish_studio_stop_app_supervisors(void)
827 ladish_studio_iterate_virtual_graphs(NULL, ladish_studio_stop_app_supervisor);
830 void ladish_studio_emit_renamed(void)
832 dbus_signal_emit(g_dbus_connection, STUDIO_OBJECT_PATH, IFACE_STUDIO, "StudioRenamed", "s", &g_studio.name);
835 static void ladish_studio_dbus_get_name(struct dbus_method_call * call_ptr)
837 method_return_new_single(call_ptr, DBUS_TYPE_STRING, &g_studio.name);
840 static void ladish_studio_dbus_rename(struct dbus_method_call * call_ptr)
842 const char * new_name;
843 char * new_name_dup;
845 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
847 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
848 dbus_error_free(&g_dbus_error);
849 return;
852 log_info("Rename studio request (%s)", new_name);
854 new_name_dup = strdup(new_name);
855 if (new_name_dup == NULL)
857 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed to allocate new name.");
858 return;
861 free(g_studio.name);
862 g_studio.name = new_name_dup;
864 method_return_new_void(call_ptr);
865 ladish_studio_emit_renamed();
868 static void ladish_studio_dbus_save(struct dbus_method_call * call_ptr)
870 log_info("Save studio request");
872 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, g_studio.name))
874 method_return_new_void(call_ptr);
878 static void ladish_studio_dbus_save_as(struct dbus_method_call * call_ptr)
880 const char * new_name;
882 log_info("SaveAs studio request");
884 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
886 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
887 dbus_error_free(&g_dbus_error);
888 return;
891 if (ladish_command_save_studio(call_ptr, &g_studio.cmd_queue, new_name))
893 method_return_new_void(call_ptr);
897 static void ladish_studio_dbus_unload(struct dbus_method_call * call_ptr)
899 log_info("Unload studio request");
901 if (ladish_command_unload_studio(call_ptr, &g_studio.cmd_queue))
903 method_return_new_void(call_ptr);
907 static void ladish_studio_dbus_stop(struct dbus_method_call * call_ptr)
909 log_info("Stop studio request");
911 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
913 if (ladish_command_stop_studio(call_ptr, &g_studio.cmd_queue))
915 method_return_new_void(call_ptr);
919 static void ladish_studio_dbus_start(struct dbus_method_call * call_ptr)
921 log_info("Start studio request");
923 g_studio.automatic = false; /* even if it was automatic, it is not anymore because user knows about it */
925 if (ladish_command_start_studio(call_ptr, &g_studio.cmd_queue))
927 method_return_new_void(call_ptr);
931 static void ladish_studio_dbus_is_started(struct dbus_method_call * call_ptr)
933 dbus_bool_t started;
935 started = g_studio.jack_graph_proxy != NULL;
937 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &started);
940 struct add_room_ports_context
942 ladish_client_handle room_client;
943 ladish_graph_handle room_graph;
946 #define add_room_ports_context_ptr ((struct add_room_ports_context *)context)
948 static
949 bool
950 ladish_studio_add_room_ports(
951 void * context,
952 ladish_port_handle port_handle,
953 const char * port_name,
954 uint32_t port_type,
955 uint32_t port_flags)
957 uuid_t uuid_in_studio;
958 uuid_t uuid_in_room;
959 char uuid_in_studio_str[37];
960 char uuid_in_room_str[37];
961 bool room_input;
962 const char * input_port;
963 const char * output_port;
965 //log_info("Studio room port \"%s\"", port_name);
967 if (JACKDBUS_PORT_IS_INPUT(port_flags))
969 JACKDBUS_PORT_CLEAR_INPUT(port_flags);
970 JACKDBUS_PORT_SET_OUTPUT(port_flags);
971 room_input = true;
973 else if (JACKDBUS_PORT_IS_OUTPUT(port_flags))
975 JACKDBUS_PORT_CLEAR_OUTPUT(port_flags);
976 JACKDBUS_PORT_SET_INPUT(port_flags);
977 room_input = false;
979 else
981 log_error("room link port with bad flags %"PRIu32, port_flags);
982 return false;
985 if (!ladish_graph_add_port(g_studio.studio_graph, add_room_ports_context_ptr->room_client, port_handle, port_name, port_type, port_flags, true))
987 log_error("ladish_graph_add_port() failed to add link port to studio graph");
988 return false;
991 ladish_graph_get_port_uuid(add_room_ports_context_ptr->room_graph, port_handle, uuid_in_room);
992 ladish_graph_get_port_uuid(g_studio.studio_graph, port_handle, uuid_in_studio);
994 uuid_unparse(uuid_in_room, uuid_in_room_str);
995 uuid_unparse(uuid_in_studio, uuid_in_studio_str);
997 if (room_input)
999 input_port = uuid_in_room_str;
1000 output_port = uuid_in_studio_str;
1001 log_info("room input port %s is linked to studio output port %s", input_port, output_port);
1003 else
1005 input_port = uuid_in_studio_str;
1006 output_port = uuid_in_room_str;
1007 log_info("studio input port %s is linked to room output port %s", input_port, output_port);
1010 if (!jmcore_proxy_create_link(port_type == JACKDBUS_PORT_TYPE_MIDI, input_port, output_port))
1012 log_error("jmcore_proxy_create_link() failed.");
1013 return false;
1016 return true;
1019 #undef add_room_ports_context_ptr
1021 static void ladish_studio_dbus_create_room(struct dbus_method_call * call_ptr)
1023 const char * room_name;
1024 const char * template_name;
1025 ladish_room_handle room;
1026 char room_dbus_name[1024];
1027 ladish_client_handle room_client;
1028 uuid_t room_uuid;
1029 ladish_graph_handle room_graph;
1030 struct add_room_ports_context context;
1032 dbus_error_init(&g_dbus_error);
1034 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &room_name, DBUS_TYPE_STRING, &template_name, DBUS_TYPE_INVALID))
1036 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1037 dbus_error_free(&g_dbus_error);
1038 goto fail;
1041 log_info("Request to create new studio room \"%s\" from template \"%s\".", room_name, template_name);
1043 room = find_room_template_by_name(template_name);
1044 if (room == NULL)
1046 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Unknown room template \"%s\"", template_name);
1047 goto fail;
1050 g_studio.room_count++;
1052 sprintf(room_dbus_name, DBUS_BASE_PATH "/Room%u", g_studio.room_count);
1054 if (!ladish_room_create(NULL, room_name, room, room_dbus_name, &room))
1056 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_room_create() failed.");
1057 goto fail_decrement_room_count;
1060 room_graph = ladish_room_get_graph(room);
1061 if (g_studio.virtualizer != NULL)
1063 ladish_virtualizer_set_graph_connection_handlers(g_studio.virtualizer, room_graph);
1066 ladish_room_get_uuid(room, room_uuid);
1068 if (!ladish_client_create(room_uuid, &room_client))
1070 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_client_create() failed.");
1071 goto fail_destroy_room;
1074 if (!ladish_graph_add_client(g_studio.studio_graph, room_client, room_name, false))
1076 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "ladish_graph_add_client() failed to add room client to studio graph.");
1077 goto fail_destroy_room_client;
1080 context.room_client = room_client;
1081 context.room_graph = room_graph;
1083 if (!ladish_room_iterate_link_ports(room, &context, ladish_studio_add_room_ports))
1085 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Creation of studio room link ports failed.");
1086 goto fail_remove_room_client;
1089 list_add_tail(ladish_room_get_list_node(room), &g_studio.rooms);
1091 ladish_studio_emit_room_appeared(room);
1093 method_return_new_void(call_ptr);
1094 return;
1096 fail_remove_room_client:
1097 ladish_graph_remove_client(g_studio.studio_graph, room_client);
1098 fail_destroy_room_client:
1099 ladish_client_destroy(room_client);
1100 fail_destroy_room:
1101 ladish_room_destroy(room);
1102 fail_decrement_room_count:
1103 g_studio.room_count--;
1104 fail:
1105 return;
1108 static void ladish_studio_dbus_get_room_list(struct dbus_method_call * call_ptr)
1110 DBusMessageIter iter, array_iter;
1111 DBusMessageIter struct_iter;
1112 struct list_head * node_ptr;
1113 ladish_room_handle room;
1115 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1116 if (call_ptr->reply == NULL)
1118 goto fail;
1121 dbus_message_iter_init_append(call_ptr->reply, &iter);
1123 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sa{sv})", &array_iter))
1125 goto fail_unref;
1128 list_for_each(node_ptr, &g_studio.rooms)
1130 room = ladish_room_from_list_node(node_ptr);
1132 if (!dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
1133 goto fail_unref;
1135 if (!ladish_studio_fill_room_info(&struct_iter, room))
1136 goto fail_unref;
1138 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
1139 goto fail_unref;
1142 if (!dbus_message_iter_close_container(&iter, &array_iter))
1144 goto fail_unref;
1147 return;
1149 fail_unref:
1150 dbus_message_unref(call_ptr->reply);
1151 call_ptr->reply = NULL;
1153 fail:
1154 log_error("Ran out of memory trying to construct method return");
1157 static void ladish_studio_dbus_delete_room(struct dbus_method_call * call_ptr)
1159 const char * name;
1160 struct list_head * node_ptr;
1161 ladish_room_handle room;
1162 uuid_t room_uuid;
1163 ladish_client_handle room_client;
1164 unsigned int running_app_count;
1166 dbus_error_init(&g_dbus_error);
1168 if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID))
1170 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1171 dbus_error_free(&g_dbus_error);
1172 return;
1175 log_info("Delete studio room request (%s)", name);
1177 list_for_each(node_ptr, &g_studio.rooms)
1179 room = ladish_room_from_list_node(node_ptr);
1180 if (strcmp(ladish_room_get_name(room), name) == 0)
1182 running_app_count = ladish_app_supervisor_get_running_app_count(ladish_room_get_app_supervisor(room));
1183 if (running_app_count != 0)
1185 /* TODO: instead of rejecting the room deletion, use the command queue and wait for room apps to stop.
1186 This requires proper "project in room" implementation because project needs to be
1187 unloaded anyway and unloading project should initiate and wait apps termination */
1188 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Cannot delete room \"%s\" because it has %u app(s) running", name, running_app_count);
1189 return;
1192 list_del(node_ptr);
1193 ladish_studio_emit_room_disappeared(room);
1195 ladish_room_get_uuid(room, room_uuid);
1196 room_client = ladish_graph_find_client_by_uuid(g_studio.studio_graph, room_uuid);
1197 ASSERT(room_client != NULL);
1198 ladish_graph_remove_client(g_studio.studio_graph, room_client);
1199 ladish_client_destroy(room_client);
1201 ladish_room_destroy(room);
1202 method_return_new_void(call_ptr);
1203 return;
1207 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": Cannot find room with name \"%s\"", call_ptr->method_name, name);
1208 return;
1211 void ladish_studio_remove_all_rooms(void)
1213 struct list_head * node_ptr;
1214 ladish_room_handle room;
1216 while (!list_empty(&g_studio.rooms))
1218 node_ptr = g_studio.rooms.next;
1219 list_del(node_ptr);
1220 room = ladish_room_from_list_node(node_ptr);
1221 ladish_studio_emit_room_disappeared(room);
1222 ladish_room_destroy(room);
1226 METHOD_ARGS_BEGIN(GetName, "Get studio name")
1227 METHOD_ARG_DESCRIBE_OUT("studio_name", "s", "Name of studio")
1228 METHOD_ARGS_END
1230 METHOD_ARGS_BEGIN(Rename, "Rename studio")
1231 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1232 METHOD_ARGS_END
1234 METHOD_ARGS_BEGIN(Save, "Save studio")
1235 METHOD_ARGS_END
1237 METHOD_ARGS_BEGIN(SaveAs, "SaveAs studio")
1238 METHOD_ARG_DESCRIBE_IN("studio_name", "s", "New name")
1239 METHOD_ARGS_END
1241 METHOD_ARGS_BEGIN(Unload, "Unload studio")
1242 METHOD_ARGS_END
1244 METHOD_ARGS_BEGIN(Start, "Start studio")
1245 METHOD_ARGS_END
1247 METHOD_ARGS_BEGIN(Stop, "Stop studio")
1248 METHOD_ARGS_END
1250 METHOD_ARGS_BEGIN(IsStarted, "Check whether studio is started")
1251 METHOD_ARG_DESCRIBE_OUT("started", "b", "Whether studio is started")
1252 METHOD_ARGS_END
1254 METHOD_ARGS_BEGIN(CreateRoom, "Create new studio room")
1255 METHOD_ARG_DESCRIBE_IN("room_name", "s", "Studio room name")
1256 METHOD_ARG_DESCRIBE_IN("room_template_name", "s", "Room template name")
1257 METHOD_ARGS_END
1259 METHOD_ARGS_BEGIN(GetRoomList, "Get list of rooms in this studio")
1260 METHOD_ARG_DESCRIBE_OUT("room_list", "a(sa{sv})", "List of studio rooms: opaths and properties")
1261 METHOD_ARGS_END
1263 METHOD_ARGS_BEGIN(DeleteRoom, "Delete studio room")
1264 METHOD_ARG_DESCRIBE_IN("room_name", "s", "Name of studio room to delete")
1265 METHOD_ARGS_END
1267 METHODS_BEGIN
1268 METHOD_DESCRIBE(GetName, ladish_studio_dbus_get_name)
1269 METHOD_DESCRIBE(Rename, ladish_studio_dbus_rename)
1270 METHOD_DESCRIBE(Save, ladish_studio_dbus_save)
1271 METHOD_DESCRIBE(SaveAs, ladish_studio_dbus_save_as)
1272 METHOD_DESCRIBE(Unload, ladish_studio_dbus_unload)
1273 METHOD_DESCRIBE(Start, ladish_studio_dbus_start)
1274 METHOD_DESCRIBE(Stop, ladish_studio_dbus_stop)
1275 METHOD_DESCRIBE(IsStarted, ladish_studio_dbus_is_started)
1276 METHOD_DESCRIBE(CreateRoom, ladish_studio_dbus_create_room)
1277 METHOD_DESCRIBE(GetRoomList, ladish_studio_dbus_get_room_list)
1278 METHOD_DESCRIBE(DeleteRoom, ladish_studio_dbus_delete_room)
1279 METHODS_END
1281 SIGNAL_ARGS_BEGIN(StudioRenamed, "Studio name changed")
1282 SIGNAL_ARG_DESCRIBE("studio_name", "s", "New studio name")
1283 SIGNAL_ARGS_END
1285 SIGNAL_ARGS_BEGIN(StudioStarted, "Studio started")
1286 SIGNAL_ARGS_END
1288 SIGNAL_ARGS_BEGIN(StudioCrashed, "Studio crashed")
1289 SIGNAL_ARGS_END
1291 SIGNAL_ARGS_BEGIN(StudioStopped, "Studio stopped")
1292 SIGNAL_ARGS_END
1294 SIGNAL_ARGS_BEGIN(RoomAppeared, "Room D-Bus object appeared")
1295 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1296 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1297 SIGNAL_ARGS_END
1299 SIGNAL_ARGS_BEGIN(RoomChanged, "Room D-Bus object changed")
1300 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1301 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1302 SIGNAL_ARGS_END
1304 SIGNAL_ARGS_BEGIN(RoomDisappeared, "Room D-Bus object disappeared")
1305 SIGNAL_ARG_DESCRIBE("opath", "s", "room object path")
1306 SIGNAL_ARG_DESCRIBE("properties", "a{sv}", "room object path and props")
1307 SIGNAL_ARGS_END
1309 SIGNALS_BEGIN
1310 SIGNAL_DESCRIBE(StudioRenamed)
1311 SIGNAL_DESCRIBE(StudioStarted)
1312 SIGNAL_DESCRIBE(StudioCrashed)
1313 SIGNAL_DESCRIBE(StudioStopped)
1314 SIGNAL_DESCRIBE(RoomAppeared)
1315 SIGNAL_DESCRIBE(RoomDisappeared)
1316 SIGNAL_DESCRIBE(RoomChanged)
1317 SIGNALS_END
1319 INTERFACE_BEGIN(g_interface_studio, IFACE_STUDIO)
1320 INTERFACE_DEFAULT_HANDLER
1321 INTERFACE_EXPOSE_METHODS
1322 INTERFACE_EXPOSE_SIGNALS
1323 INTERFACE_END