1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2010 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation save releated helper functions
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.
33 struct ladish_write_vgraph_context
37 ladish_app_supervisor_handle app_supervisor
;
41 static bool is_system_client(ladish_client_handle client
)
44 ladish_client_get_uuid(client
, uuid
);
45 return ladish_virtualizer_is_system_client(uuid
);
48 static bool is_hidden_port_interesting(ladish_app_supervisor_handle app_supervisor
, ladish_client_handle client
, ladish_port_handle port
)
51 ladish_app_handle app
;
53 if (is_system_client(client
) || ladish_port_is_link(port
))
58 /* hidden ports of external apps should not be saved */
59 /* hidden ports of stopped managed apps should be saved */
60 /* hidden ports of started managed apps should not be saved */
62 if (!ladish_port_get_app(port
, app_uuid
))
64 /* port of external app, don't save */
68 app
= ladish_app_supervisor_find_app_by_uuid(app_supervisor
, app_uuid
);
71 ASSERT_NO_PASS
; /* this should not happen because app ports are removed before app is removed */
75 return !ladish_app_is_running(app
);
78 bool ladish_write_string(int fd
, const char * string
)
85 ret
= write(fd
, string
, len
);
88 log_error("write(%d, \"%s\", %zu) failed to write file: %d (%s)", fd
, string
, len
, errno
, strerror(errno
));
91 if ((size_t)ret
!= len
)
93 log_error("write() wrote wrong byte count to file (%zd != %zu).", ret
, len
);
100 bool ladish_write_indented_string(int fd
, int indent
, const char * string
)
105 if (!ladish_write_string(fd
, LADISH_XML_BASE_INDENT
))
111 if (!ladish_write_string(fd
, string
))
119 bool ladish_write_string_escape_ex(int fd
, const char * string
, unsigned int flags
)
122 char * escaped_buffer
;
124 escaped_buffer
= malloc(max_escaped_length(strlen(string
)));
125 if (escaped_buffer
== NULL
)
127 log_error("malloc() failed to allocate buffer for escaped string");
131 escape_simple(string
, escaped_buffer
, flags
);
133 ret
= ladish_write_string(fd
, escaped_buffer
);
135 free(escaped_buffer
);
140 bool ladish_write_string_escape(int fd
, const char * string
)
142 return ladish_write_string_escape_ex(fd
, string
, LADISH_ESCAPE_FLAG_ALL
);
145 #define fd (((struct ladish_write_context *)context)->fd)
146 #define indent (((struct ladish_write_context *)context)->indent)
155 if (!ladish_write_indented_string(fd
, indent
, "<key name=\""))
160 if (!ladish_write_string(fd
, key
))
165 if (!ladish_write_string(fd
, "\">"))
170 if (!ladish_write_string(fd
, value
))
175 if (!ladish_write_string(fd
, "</key>\n"))
185 ladish_write_room_port(
187 ladish_port_handle port
,
195 const char * type_str
;
197 const char * direction_str
;
198 ladish_dict_handle dict
;
200 ladish_port_get_uuid(port
, uuid
);
201 uuid_unparse(uuid
, str
);
203 playback
= (flags
& JACKDBUS_PORT_FLAG_INPUT
) != 0;
204 ASSERT(playback
|| (flags
& JACKDBUS_PORT_FLAG_OUTPUT
) != 0); /* playback or capture */
205 ASSERT(!(playback
&& (flags
& JACKDBUS_PORT_FLAG_OUTPUT
) != 0)); /* but not both */
206 direction_str
= playback
? "playback" : "capture";
208 midi
= type
== JACKDBUS_PORT_TYPE_MIDI
;
209 ASSERT(midi
|| type
== JACKDBUS_PORT_TYPE_AUDIO
); /* midi or audio */
210 ASSERT(!(midi
&& type
== JACKDBUS_PORT_TYPE_AUDIO
)); /* but not both */
211 type_str
= midi
? "midi" : "audio";
213 log_info("saving room %s %s port '%s' (%s)", direction_str
, type_str
, name
, str
);
215 if (!ladish_write_indented_string(fd
, indent
, "<port name=\""))
220 if (!ladish_write_string_escape_ex(fd
, name
, LADISH_ESCAPE_FLAG_XML_ATTR
))
225 if (!ladish_write_string(fd
, "\" uuid=\""))
230 if (!ladish_write_string(fd
, str
))
235 if (!ladish_write_string(fd
, "\" type=\""))
240 if (!ladish_write_string(fd
, type_str
))
245 if (!ladish_write_string(fd
, "\" direction=\""))
250 if (!ladish_write_string(fd
, direction_str
))
255 dict
= ladish_port_get_dict(port
);
256 if (ladish_dict_is_empty(dict
))
258 if (!ladish_write_string(fd
, "\" />\n"))
265 if (!ladish_write_string(fd
, "\">\n"))
270 if (!ladish_write_dict(fd
, indent
+ 1, dict
))
275 if (!ladish_write_indented_string(fd
, indent
, "</port>\n"))
287 bool ladish_write_dict(int fd
, int indent
, ladish_dict_handle dict
)
289 struct ladish_write_context context
;
291 if (ladish_dict_is_empty(dict
))
297 context
.indent
= indent
+ 1;
299 if (!ladish_write_indented_string(fd
, indent
, "<dict>\n"))
304 if (!ladish_dict_iterate(dict
, &context
, write_dict_entry
))
309 if (!ladish_write_indented_string(fd
, indent
, "</dict>\n"))
317 bool ladish_write_room_link_ports(int fd
, int indent
, ladish_room_handle room
)
319 struct ladish_write_context context
;
321 ladish_check_integrity();
324 context
.indent
= indent
;
326 if (!ladish_room_iterate_link_ports(room
, &context
, ladish_write_room_port
))
328 log_error("ladish_room_iterate_link_ports() failed");
339 #define fd (((struct ladish_write_vgraph_context *)context)->fd)
340 #define indent (((struct ladish_write_vgraph_context *)context)->indent)
341 #define ctx_ptr ((struct ladish_write_vgraph_context *)context)
345 ladish_save_vgraph_client_begin(
347 ladish_graph_handle graph
,
349 ladish_client_handle client_handle
,
350 const char * client_name
,
351 void ** client_iteration_context_ptr_ptr
)
356 ctx_ptr
->client_visible
= !hidden
|| ladish_client_has_app(client_handle
);
357 if (!ctx_ptr
->client_visible
)
362 ladish_client_get_uuid(client_handle
, uuid
);
363 uuid_unparse(uuid
, str
);
365 log_info("saving vgraph client '%s' (%s)", client_name
, str
);
367 if (!ladish_write_indented_string(fd
, indent
, "<client name=\""))
372 if (!ladish_write_string_escape_ex(fd
, client_name
, LADISH_ESCAPE_FLAG_XML_ATTR
))
377 if (!ladish_write_string(fd
, "\" uuid=\""))
382 if (!ladish_write_string(fd
, str
))
387 if (!ladish_write_string(fd
, "\" naming=\""))
392 if (!ladish_write_string(fd
, "app"))
397 if (!ladish_write_string(fd
, "\">\n"))
402 if (!ladish_write_indented_string(fd
, indent
+ 1, "<ports>\n"))
412 ladish_save_vgraph_client_end(
414 ladish_graph_handle graph
,
416 ladish_client_handle client_handle
,
417 const char * client_name
,
418 void * client_iteration_context_ptr
)
420 if (!ctx_ptr
->client_visible
)
425 if (!ladish_write_indented_string(fd
, indent
+ 1, "</ports>\n"))
430 if (!ladish_write_dict(fd
, indent
+ 1, ladish_client_get_dict(client_handle
)))
435 if (!ladish_write_indented_string(fd
, indent
, "</client>\n"))
443 static bool ladish_get_vgraph_port_uuids(ladish_graph_handle vgraph
, ladish_port_handle port
, uuid_t uuid
, uuid_t link_uuid
)
447 if (vgraph
!= ladish_studio_get_studio_graph())
449 link
= false; /* room ports are saved using their fixed uuids */
453 link
= ladish_port_is_link(port
);
456 /* get the generated port uuid that is used for identification in the virtual graph */
457 ladish_graph_get_port_uuid(vgraph
, port
, uuid
);
461 if (!link
|| link_uuid
!= NULL
)
463 /* get the real port uuid that is same in both room and studio graphs */
464 ladish_port_get_uuid(port
, link
? link_uuid
: uuid
);
472 ladish_save_vgraph_port(
474 ladish_graph_handle graph
,
476 void * client_iteration_context_ptr
,
477 ladish_client_handle client_handle
,
478 const char * client_name
,
479 ladish_port_handle port_handle
,
480 const char * port_name
,
489 ladish_dict_handle dict
;
491 if (!ctx_ptr
->client_visible
)
496 /* skip hidden ports of running apps */
497 if (hidden
&& !is_hidden_port_interesting(ctx_ptr
->app_supervisor
, client_handle
, port_handle
))
502 link
= ladish_get_vgraph_port_uuids(graph
, port_handle
, uuid
, link_uuid
);
503 uuid_unparse(uuid
, str
);
506 uuid_unparse(link_uuid
, link_str
);
507 log_info("saving vgraph link port '%s':'%s' (%s link=%s)", client_name
, port_name
, str
, link_str
);
511 log_info("saving vgraph port '%s':'%s' (%s)", client_name
, port_name
, str
);
514 if (!ladish_write_indented_string(fd
, indent
+ 2, "<port name=\""))
519 if (!ladish_write_string_escape_ex(fd
, port_name
, LADISH_ESCAPE_FLAG_XML_ATTR
))
524 if (!ladish_write_string(fd
, "\" uuid=\""))
529 if (!ladish_write_string(fd
, str
))
534 if (!ladish_write_string(fd
, "\" type=\""))
539 if (!ladish_write_string(fd
, port_type
== JACKDBUS_PORT_TYPE_AUDIO
? "audio" : "midi"))
544 if (!ladish_write_string(fd
, "\" direction=\""))
549 if (!ladish_write_string(fd
, JACKDBUS_PORT_IS_INPUT(port_flags
) ? "input" : "output"))
556 if (!ladish_write_string(fd
, "\" link_uuid=\""))
561 if (!ladish_write_string(fd
, link_str
))
567 dict
= ladish_port_get_dict(port_handle
);
568 if (ladish_dict_is_empty(dict
))
570 if (!ladish_write_string(fd
, "\" />\n"))
577 if (!ladish_write_string(fd
, "\">\n"))
582 if (!ladish_write_dict(fd
, indent
+ 3, dict
))
587 if (!ladish_write_indented_string(fd
, indent
+ 2, "</port>\n"))
598 ladish_save_vgraph_connection(
600 ladish_graph_handle graph
,
602 ladish_client_handle client1
,
603 ladish_port_handle port1
,
604 ladish_client_handle client2
,
605 ladish_port_handle port2
,
606 ladish_dict_handle dict
)
612 (!is_hidden_port_interesting(ctx_ptr
->app_supervisor
, client1
, port1
) ||
613 !is_hidden_port_interesting(ctx_ptr
->app_supervisor
, client2
, port2
)))
618 log_info("saving vgraph connection");
620 if (!ladish_write_indented_string(fd
, indent
, "<connection port1=\""))
625 ladish_get_vgraph_port_uuids(graph
, port1
, uuid
, NULL
);
626 uuid_unparse(uuid
, str
);
628 if (!ladish_write_string(fd
, str
))
633 if (!ladish_write_string(fd
, "\" port2=\""))
638 ladish_get_vgraph_port_uuids(graph
, port2
, uuid
, NULL
);
639 uuid_unparse(uuid
, str
);
641 if (!ladish_write_string(fd
, str
))
646 if (ladish_dict_is_empty(dict
))
648 if (!ladish_write_string(fd
, "\" />\n"))
655 if (!ladish_write_string(fd
, "\">\n"))
660 if (!ladish_write_dict(fd
, indent
+ 1, dict
))
665 if (!ladish_write_indented_string(fd
, indent
, "</connection>\n"))
680 const char * command
,
687 const char * unescaped_string
;
688 char * escaped_string
;
689 char * escaped_buffer
;
692 log_info("saving app: name='%s', %srunning, %s, level %u, commandline='%s'", name
, running
? "" : "not ", terminal
? "terminal" : "shell", (unsigned int)level
, command
);
696 escaped_buffer
= malloc(max_escaped_length(ladish_max(strlen(name
), strlen(command
))) + 1);
697 if (escaped_buffer
== NULL
)
699 log_error("malloc() failed.");
703 if (!ladish_write_indented_string(fd
, indent
, "<application name=\""))
708 unescaped_string
= name
;
709 escaped_string
= escaped_buffer
;
710 escape(&unescaped_string
, &escaped_string
, LADISH_ESCAPE_FLAG_ALL
);
712 if (!ladish_write_string(fd
, escaped_buffer
))
717 if (!ladish_write_string(fd
, "\" terminal=\""))
722 if (!ladish_write_string(fd
, terminal
? "true" : "false"))
727 if (!ladish_write_string(fd
, "\" level=\""))
732 sprintf(buf
, "%u", (unsigned int)level
);
734 if (!ladish_write_string(fd
, buf
))
739 if (!ladish_write_string(fd
, "\" autorun=\""))
744 if (!ladish_write_string(fd
, running
? "true" : "false"))
749 if (!ladish_write_string(fd
, "\">"))
754 unescaped_string
= command
;
755 escaped_string
= escaped_buffer
;
756 escape(&unescaped_string
, &escaped_string
, LADISH_ESCAPE_FLAG_ALL
);
758 if (!ladish_write_string(fd
, escaped_buffer
))
763 if (!ladish_write_string(fd
, "</application>\n"))
771 free(escaped_buffer
);
781 bool ladish_write_vgraph(int fd
, int indent
, ladish_graph_handle vgraph
, ladish_app_supervisor_handle app_supervisor
)
783 struct ladish_write_vgraph_context context
;
785 ladish_check_integrity();
788 context
.indent
= indent
+ 1;
789 context
.app_supervisor
= app_supervisor
;
791 if (!ladish_write_indented_string(fd
, indent
, "<clients>\n"))
796 if (!ladish_graph_iterate_nodes(
799 ladish_save_vgraph_client_begin
,
800 ladish_save_vgraph_port
,
801 ladish_save_vgraph_client_end
))
803 log_error("ladish_graph_iterate_nodes() failed");
807 if (!ladish_write_indented_string(fd
, indent
, "</clients>\n"))
812 if (!ladish_write_indented_string(fd
, indent
, "<connections>\n"))
817 if (!ladish_graph_iterate_connections(vgraph
, &context
, ladish_save_vgraph_connection
))
819 log_error("ladish_graph_iterate_connections() failed");
823 if (!ladish_write_indented_string(fd
, indent
, "</connections>\n"))
828 if (!ladish_write_indented_string(fd
, indent
, "<applications>\n"))
833 if (!ladish_app_supervisor_enum(app_supervisor
, &context
, ladish_save_app
))
838 if (!ladish_write_indented_string(fd
, indent
, "</applications>\n"))
846 /********************/
847 /* write jack graph */
848 /********************/
850 struct ladish_write_jack_context
854 ladish_graph_handle vgraph_filter
;
855 ladish_app_supervisor_handle app_supervisor
;
856 bool client_vgraph_match
;
861 static bool ladish_save_jack_client_write_prolog(int fd
, int indent
, ladish_client_handle client_handle
, const char * client_name
)
866 ladish_client_get_uuid(client_handle
, uuid
);
867 uuid_unparse(uuid
, str
);
869 log_info("saving jack client '%s' (%s)", client_name
, str
);
871 if (!ladish_write_indented_string(fd
, indent
, "<client name=\""))
876 if (!ladish_write_string_escape_ex(fd
, client_name
, LADISH_ESCAPE_FLAG_XML_ATTR
))
881 if (!ladish_write_string(fd
, "\" uuid=\""))
886 if (!ladish_write_string(fd
, str
))
891 if (!ladish_write_string(fd
, "\">\n"))
896 if (!ladish_write_indented_string(fd
, indent
+ 1, "<ports>\n"))
904 #define fd (((struct ladish_write_jack_context *)context)->fd)
905 #define indent (((struct ladish_write_jack_context *)context)->indent)
906 #define ctx_ptr ((struct ladish_write_jack_context *)context)
910 ladish_save_jack_client_begin(
912 ladish_graph_handle graph_handle
,
914 ladish_client_handle client_handle
,
915 const char * client_name
,
916 void ** client_iteration_context_ptr_ptr
)
920 vgraph
= ladish_client_get_vgraph(client_handle
);
921 ctx_ptr
->client_vgraph_match
= vgraph
== ctx_ptr
->vgraph_filter
;
922 ctx_ptr
->a2j
= ladish_virtualizer_is_a2j_client(client_handle
);
924 /* for the a2j client vgraph is always the studio graph.
925 However if studio has no a2j ports, lets not write a2j client.
926 If there is a a2j port that matched the vgraph, the prolog will get written anyway */
927 ctx_ptr
->client_visible
=
929 ladish_client_has_app(client_handle
)) &&
930 ctx_ptr
->client_vgraph_match
&&
932 if (!ctx_ptr
->client_visible
)
937 return ladish_save_jack_client_write_prolog(fd
, indent
, client_handle
, client_name
);
942 ladish_save_jack_client_end(
944 ladish_graph_handle graph_handle
,
946 ladish_client_handle client_handle
,
947 const char * client_name
,
948 void * client_iteration_context_ptr
)
950 if (!ctx_ptr
->client_visible
)
955 if (!ladish_write_indented_string(fd
, indent
+ 1, "</ports>\n"))
960 if (!ladish_write_indented_string(fd
, indent
, "</client>\n"))
970 ladish_save_jack_port(
972 ladish_graph_handle graph_handle
,
974 void * client_iteration_context_ptr
,
975 ladish_client_handle client_handle
,
976 const char * client_name
,
977 ladish_port_handle port_handle
,
978 const char * port_name
,
985 if (hidden
&& !ladish_port_has_app(port_handle
))
990 /* check vgraph for a2j ports */
991 if (ctx_ptr
->a2j
&& ctx_ptr
->vgraph_filter
== ladish_port_get_vgraph(port_handle
))
993 if (!ctx_ptr
->client_visible
)
995 if (!ladish_save_jack_client_write_prolog(fd
, indent
, client_handle
, client_name
))
1000 ctx_ptr
->client_visible
= true;
1004 if (!ctx_ptr
->client_visible
)
1009 /* skip hidden ports of running apps */
1010 if (hidden
&& !is_hidden_port_interesting(ctx_ptr
->app_supervisor
, client_handle
, port_handle
))
1015 ladish_port_get_uuid(port_handle
, uuid
);
1016 uuid_unparse(uuid
, str
);
1018 log_info("saving jack port '%s':'%s' (%s)", client_name
, port_name
, str
);
1020 if (!ladish_write_indented_string(fd
, indent
+ 2, "<port name=\""))
1025 if (!ladish_write_string_escape_ex(fd
, port_name
, LADISH_ESCAPE_FLAG_XML_ATTR
))
1030 if (!ladish_write_string(fd
, "\" uuid=\""))
1035 if (!ladish_write_string(fd
, str
))
1040 if (!ladish_write_string(fd
, "\" />\n"))
1052 bool ladish_write_jgraph(int fd
, int indent
, ladish_graph_handle vgraph
, ladish_app_supervisor_handle app_supervisor
)
1054 struct ladish_write_jack_context context
;
1056 ladish_check_integrity();
1058 if (!ladish_write_indented_string(fd
, indent
, "<clients>\n"))
1064 context
.indent
= indent
+ 1;
1065 context
.vgraph_filter
= vgraph
;
1066 context
.app_supervisor
= app_supervisor
;
1068 if (!ladish_graph_iterate_nodes(
1069 ladish_studio_get_jack_graph(),
1071 ladish_save_jack_client_begin
,
1072 ladish_save_jack_port
,
1073 ladish_save_jack_client_end
))
1075 log_error("ladish_graph_iterate_nodes() failed");
1079 if (!ladish_write_indented_string(fd
, indent
, "</clients>\n"))