daemon: handle apps with same name in different vgraphs
[ladish.git] / daemon / virtualizer.c
blob8e54ee4af040c1da8dd00c48547e5d21d88fac58
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 implementation of the graph virtualizer 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.
27 #include "virtualizer.h"
28 #include "../dbus_constants.h"
29 #include "../proxies/a2j_proxy.h"
30 #include "../proxies/jmcore_proxy.h"
31 #include "procfs.h"
32 #include "app_supervisor.h"
33 #include "studio_internal.h"
34 #include "../catdup.h"
35 #include "room.h"
36 #include "studio.h"
38 struct virtualizer
40 graph_proxy_handle jack_graph_proxy;
41 ladish_graph_handle jack_graph;
42 uint64_t system_client_id;
43 unsigned int our_clients_count;
46 /* 47c1cd18-7b21-4389-bec4-6e0658e1d6b1 */
47 UUID_DEFINE(g_system_capture_uuid,0x47,0xC1,0xCD,0x18,0x7B,0x21,0x43,0x89,0xBE,0xC4,0x6E,0x06,0x58,0xE1,0xD6,0xB1);
49 /* b2a0bb06-28d8-4bfe-956e-eb24378f9629 */
50 UUID_DEFINE(g_system_playback_uuid,0xB2,0xA0,0xBB,0x06,0x28,0xD8,0x4B,0xFE,0x95,0x6E,0xEB,0x24,0x37,0x8F,0x96,0x29);
52 /* be23a242-e2b2-11de-b795-002618af5e42 */
53 UUID_DEFINE(g_a2j_uuid,0xBE,0x23,0xA2,0x42,0xE2,0xB2,0x11,0xDE,0xB7,0x95,0x00,0x26,0x18,0xAF,0x5E,0x42);
55 struct app_find_context
57 pid_t pid;
58 char * app_name;
59 uuid_t app_uuid;
60 ladish_graph_handle graph;
63 #define app_find_context_ptr ((struct app_find_context *)context)
65 static bool get_app_properties_from_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
67 pid_t pid;
68 ladish_app_handle app;
70 ASSERT(app_find_context_ptr->app_name == NULL); /* we stop iteration when app is found */
72 //log_info("checking app supervisor \"%s\" for pid %llu", ladish_app_supervisor_get_name(app_supervisor), (unsigned long long)pid);
74 pid = app_find_context_ptr->pid;
77 app = ladish_app_supervisor_find_app_by_pid(app_supervisor, pid);
78 if (app != NULL)
79 break;
81 pid = (pid_t)procfs_get_process_parent((unsigned long long)pid);
82 #if 0
83 if (pid != 0)
85 log_info("parent pid %llu", (unsigned long long)pid);
87 #endif
89 while (pid != 0);
91 if (app == NULL)
92 { /* app not found in current supervisor */
93 return true; /* continue app supervisor iteration */
96 app_find_context_ptr->app_name = strdup(ladish_app_get_name(app));
97 if (app_find_context_ptr->app_name == NULL)
99 log_error("strdup() failed for app name '%s'", ladish_app_get_name(app));
101 else
103 ladish_app_get_uuid(app, app_find_context_ptr->app_uuid);
104 app_find_context_ptr->pid = pid;
105 app_find_context_ptr->graph = graph;
108 return false; /* stop app supervisor iteration */
111 #undef app_find_context_ptr
113 static char * get_app_properties(struct virtualizer * virtualizer_ptr, uint64_t client_id, pid_t pid, ladish_graph_handle * graph_ptr, uuid_t app_uuid)
115 struct app_find_context context;
117 context.pid = (pid_t)pid;
118 context.app_name = NULL;
119 context.graph = NULL;
120 uuid_clear(context.app_uuid);
122 ladish_studio_iterate_virtual_graphs(&context, get_app_properties_from_supervisor);
124 if (context.app_name != NULL)
126 ASSERT(context.graph != NULL);
127 ASSERT(!uuid_is_null(context.app_uuid));
128 *graph_ptr = context.graph;
129 uuid_copy(app_uuid, context.app_uuid);
132 return context.app_name;
135 struct find_link_port_context
137 uuid_t uuid;
138 uint64_t jack_id;
139 ladish_port_handle port;
140 ladish_graph_handle graph;
143 #define find_link_port_context_ptr ((struct find_link_port_context *)context)
145 static bool find_link_port_vgraph_callback_by_uuid(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
147 ladish_port_handle port;
149 port = ladish_graph_find_port_by_uuid(graph, find_link_port_context_ptr->uuid, true);
150 if (port != NULL)
152 find_link_port_context_ptr->port = port;
153 find_link_port_context_ptr->graph = graph;
154 return false;
157 return true; /* continue vgraph iteration */
160 static bool find_link_port_vgraph_callback_by_jack_id(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
162 ladish_port_handle port;
163 bool room;
165 log_info("searching link port with jack id %"PRIu64" in graph %s", find_link_port_context_ptr->jack_id, ladish_graph_get_description(graph));
167 room = graph != g_studio.studio_graph;
169 port = ladish_graph_find_port_by_jack_id(graph, find_link_port_context_ptr->jack_id, room, !room);
170 if (port != NULL)
172 find_link_port_context_ptr->port = port;
173 find_link_port_context_ptr->graph = graph;
174 return false;
177 return true; /* continue vgraph iteration */
180 #undef find_link_port_context_ptr
182 static ladish_graph_handle find_link_port_vgraph_by_uuid(struct virtualizer * virtualizer_ptr, const char * port_name, ladish_port_handle * port_ptr)
184 struct find_link_port_context context;
186 uuid_parse(port_name, context.uuid);
187 context.graph = NULL;
188 context.port = NULL;
190 ladish_studio_iterate_virtual_graphs(&context, find_link_port_vgraph_callback_by_uuid);
192 if (port_ptr != NULL && context.graph != NULL)
194 *port_ptr = context.port;
197 return context.graph;
200 static ladish_graph_handle find_link_port_vgraph_by_jack_id(struct virtualizer * virtualizer_ptr, uint64_t jack_id, ladish_port_handle * port_ptr)
202 struct find_link_port_context context;
204 context.jack_id = jack_id;
205 context.graph = NULL;
206 context.port = NULL;
208 ladish_studio_iterate_virtual_graphs(&context, find_link_port_vgraph_callback_by_jack_id);
210 if (port_ptr != NULL && context.graph != NULL)
212 *port_ptr = context.port;
215 return context.graph;
218 static
219 bool
220 lookup_port(
221 struct virtualizer * virtualizer_ptr,
222 uint64_t port_id,
223 ladish_port_handle * port_ptr,
224 ladish_graph_handle * vgraph_ptr)
226 ladish_port_handle port;
227 ladish_client_handle jclient;
228 ladish_graph_handle vgraph;
230 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
231 if (port == NULL)
233 log_error("Unknown JACK port with id %"PRIu64" (dis)connected", port_id);
234 return false;
237 jclient = ladish_graph_get_port_client(virtualizer_ptr->jack_graph, port);
238 if (jclient == NULL)
240 log_error("Port %"PRIu64" without jack client was (dis)connected", port_id);
241 return false;
244 vgraph = ladish_client_get_vgraph(jclient);
245 if (vgraph == NULL)
247 vgraph = find_link_port_vgraph_by_jack_id(virtualizer_ptr, port_id, NULL);
248 if (vgraph == NULL)
250 log_error("Cannot find vgraph for (dis)connected jmcore port");
251 return false;
254 log_info("link port found in graph %s", ladish_graph_get_description(vgraph));
257 *port_ptr = port;
258 *vgraph_ptr = vgraph;
259 return true;
262 #define virtualizer_ptr ((struct virtualizer *)context)
264 static void clear(void * context)
266 log_info("clear");
269 static void client_appeared(void * context, uint64_t id, const char * jack_name)
271 ladish_client_handle client;
272 const char * a2j_name;
273 bool is_a2j;
274 char * app_name;
275 uuid_t app_uuid;
276 const char * name;
277 pid_t pid;
278 ladish_graph_handle graph;
279 bool jmcore;
281 log_info("client_appeared(%"PRIu64", %s)", id, jack_name);
283 a2j_name = a2j_proxy_get_jack_client_name_cached();
284 is_a2j = a2j_name != NULL && strcmp(a2j_name, jack_name) == 0;
286 name = jack_name;
287 app_name = NULL;
288 graph = NULL;
289 jmcore = false;
291 if (!graph_proxy_get_client_pid(virtualizer_ptr->jack_graph_proxy, id, &pid))
293 log_info("client %"PRIu64" pid is unknown", id);
295 else
297 log_info("client pid is %"PRId64, (int64_t)pid);
299 if (pid != 0) /* skip internal clients that will match the pending clients in the graph, both have zero pid */
301 jmcore = pid == jmcore_proxy_get_pid_cached();
302 if (jmcore)
304 log_info("jmcore client appeared");
306 else
308 app_name = get_app_properties(virtualizer_ptr, id, pid, &graph, app_uuid);
309 if (app_name != NULL)
311 log_info("app name is '%s'", app_name);
312 name = app_name;
318 if (!jmcore)
320 if (is_a2j)
322 client = ladish_graph_find_client_by_uuid(virtualizer_ptr->jack_graph, g_a2j_uuid);
324 else
326 client = ladish_graph_find_client_by_app(virtualizer_ptr->jack_graph, app_uuid);
327 if (client == NULL)
329 log_info("Lookup by app uuid failed, attempting lookup by name '%s'", name);
330 client = ladish_graph_find_client_by_name(virtualizer_ptr->jack_graph, name, true);
334 if (client != NULL)
336 log_info("found existing client");
337 if (ladish_client_get_jack_id(client) != 0)
339 log_error("Ignoring client with duplicate name '%s' ('%s')", name, jack_name);
340 goto free_app_name;
343 ladish_client_set_jack_id(client, id);
344 ladish_graph_show_client(virtualizer_ptr->jack_graph, client);
345 goto done;
349 if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, &client))
351 log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, jack_name);
352 goto free_app_name;
355 ladish_client_set_jack_id(client, id);
357 if (!ladish_graph_add_client(virtualizer_ptr->jack_graph, client, name, false))
359 log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
360 ladish_client_destroy(client);
361 goto free_app_name;
364 done:
365 if (strcmp(jack_name, "system") == 0)
367 virtualizer_ptr->system_client_id = id;
370 if (app_name != NULL)
372 ladish_client_set_pid(client, pid);
373 ladish_client_set_app(client, app_uuid);
374 ASSERT(graph);
375 ladish_client_set_vgraph(client, graph);
376 virtualizer_ptr->our_clients_count++;
378 else if (jmcore)
380 ladish_client_set_pid(client, pid);
381 ASSERT(ladish_client_get_vgraph(client) == NULL);
383 else
385 /* unknown and internal clients appear in the studio graph */
386 ladish_client_set_vgraph(client, g_studio.studio_graph);
389 free_app_name:
390 if (app_name != NULL)
392 free(app_name);
396 static void client_disappeared(void * context, uint64_t id)
398 ladish_client_handle client;
399 pid_t pid;
400 ladish_graph_handle vgraph;
402 log_info("client_disappeared(%"PRIu64")", id);
404 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, id);
405 if (client == NULL)
407 log_error("Unknown JACK client with id %"PRIu64" disappeared", id);
408 return;
411 log_info("client disappeared: '%s'", ladish_graph_get_client_name(virtualizer_ptr->jack_graph, client));
413 vgraph = ladish_client_get_vgraph(client);
415 pid = ladish_client_get_pid(client);
416 if (pid != 0 && pid != jmcore_proxy_get_pid_cached())
418 virtualizer_ptr->our_clients_count--;
421 if (id == virtualizer_ptr->system_client_id)
423 virtualizer_ptr->system_client_id = 0;
426 if (vgraph != NULL && ladish_graph_is_persist(vgraph)) /* if client is supposed to be persisted */
428 ladish_client_set_jack_id(client, 0);
429 ladish_graph_hide_client(virtualizer_ptr->jack_graph, client);
431 else
433 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client);
434 ladish_client_destroy(client);
435 /* no need to clear vclient interlink because it either does not exist (vgraph is NULL) or
436 * it will be destroyed before it is accessed (persist flag is cleared on room deletion) */
440 static
441 void
442 port_appeared(
443 void * context,
444 uint64_t client_id,
445 uint64_t port_id,
446 const char * real_jack_port_name,
447 bool is_input,
448 bool is_terminal,
449 bool is_midi)
451 ladish_client_handle jack_client;
452 ladish_client_handle vclient;
453 ladish_port_handle port;
454 uint32_t type;
455 uint32_t flags;
456 const char * jack_client_name;
457 bool is_a2j;
458 uuid_t jclient_uuid;
459 uuid_t vclient_uuid;
460 char * alsa_client_name;
461 char * alsa_port_name;
462 char * a2j_fake_jack_port_name = NULL;
463 uint32_t alsa_client_id;
464 const char * jack_port_name;
465 const char * vport_name;
466 ladish_graph_handle vgraph;
468 log_info("port_appeared(%"PRIu64", %"PRIu64", %s (%s, %s))", client_id, port_id, real_jack_port_name, is_input ? "in" : "out", is_midi ? "midi" : "audio");
470 type = is_midi ? JACKDBUS_PORT_TYPE_MIDI : JACKDBUS_PORT_TYPE_AUDIO;
471 flags = is_input ? JACKDBUS_PORT_FLAG_INPUT : JACKDBUS_PORT_FLAG_OUTPUT;
472 if (is_terminal)
474 flags |= JACKDBUS_PORT_FLAG_TERMINAL;
477 /********************/
478 /* gather info about the appeared port */
480 jack_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
481 if (jack_client == NULL)
483 log_error("Port of unknown JACK client with id %"PRIu64" appeared", client_id);
484 goto exit;
487 /* find the virtual graph that owns the app that owns the client that owns the appeared port */
488 vgraph = ladish_client_get_vgraph(jack_client);
489 if (vgraph == NULL)
491 vgraph = find_link_port_vgraph_by_uuid(virtualizer_ptr, real_jack_port_name, &port);
492 if (vgraph == NULL)
494 log_error("Cannot find vgraph for appeared jmcore port '%s'", real_jack_port_name);
495 goto exit;
498 /* jmcore port appeared */
500 log_info("jmcore port appeared in vgraph %s", ladish_graph_get_description(vgraph));
502 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, real_jack_port_name, type, flags, false))
504 log_error("ladish_graph_add_port() failed.");
505 goto exit;
508 if (vgraph == g_studio.studio_graph)
510 ladish_port_set_jack_id(port, port_id);
512 else
514 ladish_port_set_jack_id_room(port, port_id);
517 vclient = ladish_graph_get_port_client(vgraph, port);
518 if (vclient == NULL)
520 log_error("link port client not found in vgraph %s", ladish_graph_get_description(vgraph));
521 ASSERT_NO_PASS;
522 goto exit;
525 ladish_graph_show_port(vgraph, port);
526 goto exit;
529 jack_client_name = ladish_graph_get_client_name(virtualizer_ptr->jack_graph, jack_client);
531 ladish_client_get_uuid(jack_client, jclient_uuid);
532 is_a2j = uuid_compare(jclient_uuid, g_a2j_uuid) == 0;
533 if (is_a2j)
535 log_info("a2j port appeared");
536 if (!a2j_proxy_map_jack_port(real_jack_port_name, &alsa_client_name, &alsa_port_name, &alsa_client_id))
538 is_a2j = false;
539 alsa_client_name = catdup("FAILED ", jack_client_name);
540 if (alsa_client_name == NULL)
542 log_error("catdup failed to duplicate a2j jack client name after map failure");
543 goto exit;
546 alsa_port_name = strdup(real_jack_port_name);
547 if (alsa_port_name == NULL)
549 log_error("catdup failed to duplicate a2j jack port name after map failure");
550 free(alsa_client_name);
551 goto exit;
554 else
556 log_info("a2j: '%s':'%s' (%"PRIu32")", alsa_client_name, alsa_port_name, alsa_client_id);
559 a2j_fake_jack_port_name = catdup4(alsa_client_name, is_input ? " (playback)" : " (capture)", ": ", alsa_port_name);
560 if (a2j_fake_jack_port_name == NULL)
562 log_error("catdup4() failed");
563 goto free_alsa_names;
566 jack_port_name = a2j_fake_jack_port_name;
568 else
570 jack_port_name = real_jack_port_name;
573 /********************/
575 /* search (by name) the appeared port in jack graph
576 * if found - show it in both graphs.
577 * if not found - create new port and add it to the jack graph.
578 * Then process to adding it to virtual graph */
580 port = ladish_graph_find_port_by_name(virtualizer_ptr->jack_graph, jack_client, jack_port_name);
581 if (port != NULL)
583 log_info("found existing port");
585 if (ladish_port_get_jack_id(port) != 0)
587 log_error("Ignoring duplicate JACK port '%s':'%s'", jack_client_name, jack_port_name);
588 goto free_alsa_names;
591 ladish_port_set_jack_id(port, port_id);
592 ladish_graph_adjust_port(virtualizer_ptr->jack_graph, port, type, flags);
593 ladish_graph_show_port(virtualizer_ptr->jack_graph, port);
595 vclient = ladish_graph_get_port_client(vgraph, port);
596 if (vclient == NULL)
598 log_error("JACK port not found in virtual graph");
599 ASSERT_NO_PASS;
600 goto free_alsa_names;
603 ladish_client_set_jack_id(vclient, client_id);
604 ladish_graph_adjust_port(vgraph, port, type, flags);
605 ladish_graph_show_port(vgraph, port);
606 goto free_alsa_names;
609 if (!ladish_port_create(NULL, false, &port))
611 log_error("ladish_port_create() failed.");
612 goto free_alsa_names;
615 /* set port jack id so invisible connections to/from it can be restored */
616 ladish_port_set_jack_id(port, port_id);
618 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, jack_port_name, type, flags, false))
620 log_error("ladish_graph_add_port() failed.");
621 ladish_port_destroy(port);
622 goto free_alsa_names;
625 /********************/
626 /* find/create the virtual client where port will be added */
628 if (is_a2j)
630 vclient = ladish_graph_find_client_by_name(vgraph, alsa_client_name, false);
631 if (vclient == NULL)
633 if (!ladish_client_create(NULL, &vclient))
635 log_error("ladish_client_create() failed.");
636 goto free_alsa_names;
639 if (!ladish_graph_add_client(vgraph, vclient, alsa_client_name, false))
641 log_error("ladish_graph_add_client() failed.");
642 ladish_client_destroy(vclient);
643 goto free_alsa_names;
647 else if (client_id == virtualizer_ptr->system_client_id)
649 log_info("system client port appeared");
651 if (!is_input)
652 { /* output capture port */
654 vclient = ladish_graph_find_client_by_uuid(vgraph, g_system_capture_uuid);
655 if (vclient == NULL)
657 if (!ladish_client_create(g_system_capture_uuid, &vclient))
659 log_error("ladish_client_create() failed.");
660 goto free_alsa_names;
663 if (!ladish_graph_add_client(vgraph, vclient, "Hardware Capture", false))
665 log_error("ladish_graph_add_client() failed.");
666 ladish_client_destroy(vclient);
667 goto free_alsa_names;
671 else
672 { /* input playback port */
673 vclient = ladish_graph_find_client_by_uuid(vgraph, g_system_playback_uuid);
674 if (vclient == NULL)
676 if (!ladish_client_create(g_system_playback_uuid, &vclient))
678 log_error("ladish_client_create() failed.");
679 goto free_alsa_names;
682 if (!ladish_graph_add_client(vgraph, vclient, "Hardware Playback", false))
684 ladish_client_destroy(vclient);
685 goto free_alsa_names;
690 else
691 { /* non-system client */
692 log_info("non-system client port appeared");
694 if (ladish_client_get_interlink(jack_client, vclient_uuid))
696 vclient = ladish_graph_find_client_by_uuid(vgraph, vclient_uuid);
697 ASSERT(vclient != NULL);
699 else
701 log_info("creating new vclient");
702 if (!ladish_client_create(NULL, &vclient))
704 log_error("ladish_client_create() failed.");
705 goto free_alsa_names;
708 ladish_client_interlink(vclient, jack_client);
710 if (!ladish_graph_add_client(vgraph, vclient, jack_client_name, false))
712 log_error("ladish_graph_add_client() failed to add client '%s' to virtual graph", jack_client_name);
713 ladish_client_destroy(vclient);
714 goto free_alsa_names;
719 /********************/
720 /* add newly appeared port to the virtual graph */
722 if (is_a2j)
724 vport_name = alsa_port_name;
726 else
728 vport_name = jack_port_name;
731 if (!ladish_graph_add_port(vgraph, vclient, port, vport_name, type, flags, false))
733 log_error("ladish_graph_add_port() failed.");
734 goto free_alsa_names;
737 free_alsa_names:
738 if (a2j_fake_jack_port_name != NULL)
740 free(a2j_fake_jack_port_name);
743 if (is_a2j)
745 free(alsa_client_name);
746 free(alsa_port_name);
749 exit:
750 return;
753 static void port_disappeared(void * context, uint64_t client_id, uint64_t port_id)
755 ladish_client_handle jclient;
756 ladish_client_handle vclient;
757 ladish_port_handle port;
758 ladish_graph_handle vgraph;
759 bool jmcore;
761 log_info("port_disappeared(%"PRIu64", %"PRIu64")", client_id, port_id);
763 jclient = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
764 if (jclient == NULL)
766 log_error("Port of unknown JACK client with id %"PRIu64" disappeared", client_id);
767 return;
770 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
771 if (port == NULL)
773 log_error("Unknown JACK port with id %"PRIu64" disappeared", port_id);
774 return;
777 /* find the virtual graph that owns the app that owns the client that owns the disappeared port */
778 jmcore = false;
779 vgraph = ladish_client_get_vgraph(jclient);
780 if (vgraph == NULL)
782 vgraph = find_link_port_vgraph_by_uuid(virtualizer_ptr, ladish_graph_get_port_name(virtualizer_ptr->jack_graph, port), NULL);
783 if (vgraph == NULL)
785 log_error("Cannot find vgraph for disappeared jmcore port");
786 ASSERT_NO_PASS;
787 return;
790 jmcore = true;
791 ladish_graph_remove_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
794 if (ladish_graph_is_persist(vgraph)) /* if port is supposed to be persisted */
796 if (!jmcore)
798 ladish_port_set_jack_id(port, 0);
799 ladish_graph_hide_port(virtualizer_ptr->jack_graph, port);
801 if (vgraph != NULL)
803 ladish_graph_hide_port(vgraph, port);
804 vclient = ladish_graph_get_port_client(vgraph, port);
805 if (ladish_graph_client_looks_empty(vgraph, vclient))
807 ladish_graph_hide_client(vgraph, vclient);
811 else
813 if (!jmcore)
815 ladish_graph_remove_port(virtualizer_ptr->jack_graph, port);
818 if (vgraph != NULL)
820 vclient = ladish_graph_remove_port(vgraph, port);
821 if (vclient != NULL)
823 if (ladish_graph_client_is_empty(vgraph, vclient))
825 ladish_graph_remove_client(vgraph, vclient);
826 ladish_client_clear_interlink(jclient);
833 static void port_renamed(void * context, uint64_t client_id, uint64_t port_id, const char * old_port_name, const char * new_port_name)
835 ladish_client_handle client;
836 ladish_port_handle port;
837 ladish_graph_handle vgraph;
839 log_info("port_renamed(%"PRIu64", '%s', '%s')", port_id, old_port_name, new_port_name);
841 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
842 if (client == NULL)
844 log_error("Port of unknown JACK client with id %"PRIu64" was renamed", client_id);
845 return;
848 /* find the virtual graph that owns the app that owns the client that owns the renamed port */
849 vgraph = ladish_client_get_vgraph(client);
851 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
852 if (port == NULL)
854 log_error("Unknown JACK port with id %"PRIu64" was renamed", port_id);
855 return;
858 if (!ladish_graph_rename_port(virtualizer_ptr->jack_graph, port, new_port_name))
860 log_error("renaming of port in jack graph failed");
863 if (!ladish_graph_rename_port(vgraph, port, new_port_name))
865 log_error("renaming of port in virtual graph failed");
869 static bool ports_connect_request(void * context, ladish_graph_handle graph_handle, ladish_port_handle port1, ladish_port_handle port2)
871 uint64_t port1_id;
872 uint64_t port2_id;
874 ASSERT(ladish_graph_get_opath(graph_handle)); /* studio or room virtual graph */
875 log_info("virtualizer: ports connect request");
877 if (graph_handle == g_studio.studio_graph)
879 port1_id = ladish_port_get_jack_id(port1);
880 port2_id = ladish_port_get_jack_id(port2);
882 else
884 port1_id = ladish_port_get_jack_id_room(port1);
885 port2_id = ladish_port_get_jack_id_room(port2);
888 graph_proxy_connect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
890 return true;
893 static bool ports_disconnect_request(void * context, ladish_graph_handle graph_handle, uint64_t connection_id)
895 ladish_port_handle port1;
896 ladish_port_handle port2;
897 uint64_t port1_id;
898 uint64_t port2_id;
900 ASSERT(ladish_graph_get_opath(graph_handle)); /* studio or room virtual graph */
901 log_info("virtualizer: ports disconnect request");
903 if (!ladish_graph_get_connection_ports(graph_handle, connection_id, &port1, &port2))
905 log_error("cannot find ports that are disconnect-requested");
906 ASSERT_NO_PASS;
907 return false;
910 if (graph_handle == g_studio.studio_graph)
912 port1_id = ladish_port_get_jack_id(port1);
913 port2_id = ladish_port_get_jack_id(port2);
915 else
917 port1_id = ladish_port_get_jack_id_room(port1);
918 port2_id = ladish_port_get_jack_id_room(port2);
921 graph_proxy_disconnect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
923 return true;
926 static void ports_connected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
928 ladish_port_handle port1;
929 ladish_port_handle port2;
930 uint64_t connection_id;
931 ladish_graph_handle vgraph1;
932 ladish_graph_handle vgraph2;
934 log_info("ports_connected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
936 if (!lookup_port(virtualizer_ptr, port1_id, &port1, &vgraph1))
938 return;
941 if (!lookup_port(virtualizer_ptr, port2_id, &port2, &vgraph2))
943 return;
946 if (vgraph1 != vgraph2)
948 /* TODO */
949 log_error("ignoring connection with endpoints in different vgraphs");
950 return;
953 ladish_graph_add_connection(virtualizer_ptr->jack_graph, port1, port2, false);
955 if (ladish_graph_find_connection(vgraph1, port1, port2, &connection_id))
957 log_info("showing hidden virtual connection");
958 ladish_graph_show_connection(vgraph1, connection_id);
960 else
962 log_info("creating new virtual connection");
963 ladish_graph_add_connection(vgraph1, port1, port2, false);
967 static void ports_disconnected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
969 ladish_port_handle port1;
970 ladish_port_handle port2;
971 uint64_t connection_id;
972 ladish_graph_handle vgraph1;
973 ladish_graph_handle vgraph2;
975 log_info("ports_disconnected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
977 if (!lookup_port(virtualizer_ptr, port1_id, &port1, &vgraph1))
979 return;
982 if (!lookup_port(virtualizer_ptr, port2_id, &port2, &vgraph2))
984 return;
987 if (vgraph1 != vgraph2)
989 /* TODO */
990 log_error("ignoring connection with endpoints in different vgraphs");
991 return;
994 if (ladish_graph_find_connection(virtualizer_ptr->jack_graph, port1, port2, &connection_id))
996 ladish_graph_remove_connection(virtualizer_ptr->jack_graph, connection_id, true);
998 else
1000 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the JACK graph", client1_id, port1_id, client2_id, port2_id);
1003 if (ladish_graph_find_connection(vgraph1, port1, port2, &connection_id))
1005 ladish_graph_remove_connection(vgraph1, connection_id, false);
1007 else
1009 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the virtual graph", client1_id, port1_id, client2_id, port2_id);
1013 #undef virtualizer_ptr
1015 bool
1016 ladish_virtualizer_create(
1017 graph_proxy_handle jack_graph_proxy,
1018 ladish_graph_handle jack_graph,
1019 ladish_virtualizer_handle * handle_ptr)
1021 struct virtualizer * virtualizer_ptr;
1023 virtualizer_ptr = malloc(sizeof(struct virtualizer));
1024 if (virtualizer_ptr == NULL)
1026 log_error("malloc() failed for struct virtualizer");
1027 return false;
1030 virtualizer_ptr->jack_graph_proxy = jack_graph_proxy;
1031 virtualizer_ptr->jack_graph = jack_graph;
1032 virtualizer_ptr->system_client_id = 0;
1033 virtualizer_ptr->our_clients_count = 0;
1035 if (!graph_proxy_attach(
1036 jack_graph_proxy,
1037 virtualizer_ptr,
1038 clear,
1039 client_appeared,
1040 NULL, /* jackdbus does not have client rename functionality (yet) */
1041 client_disappeared,
1042 port_appeared,
1043 port_renamed,
1044 port_disappeared,
1045 ports_connected,
1046 ports_disconnected))
1048 free(virtualizer_ptr);
1049 return false;
1052 *handle_ptr = (ladish_virtualizer_handle)virtualizer_ptr;
1053 return true;
1056 #define virtualizer_ptr ((struct virtualizer *)handle)
1058 void
1059 ladish_virtualizer_set_graph_connection_handlers(
1060 ladish_virtualizer_handle handle,
1061 ladish_graph_handle graph)
1063 ladish_graph_set_connection_handlers(graph, virtualizer_ptr, ports_connect_request, ports_disconnect_request);
1066 unsigned int
1067 ladish_virtualizer_get_our_clients_count(
1068 ladish_virtualizer_handle handle)
1070 return virtualizer_ptr->our_clients_count;
1073 bool
1074 ladish_virtualizer_is_hidden_app(
1075 ladish_graph_handle jack_graph,
1076 const uuid_t app_uuid,
1077 const char * app_name)
1079 ladish_client_handle jclient;
1080 ladish_graph_handle vgraph;
1081 uuid_t vclient_uuid;
1082 ladish_client_handle vclient;
1083 bool is_empty;
1084 uuid_t jclient_uuid;
1085 bool is_a2j;
1087 //ladish_graph_dump(g_studio.jack_graph);
1089 jclient = ladish_graph_find_client_by_app(jack_graph, app_uuid);
1090 if (jclient == NULL)
1092 log_info("App without JACK client is treated as hidden one");
1093 return true;
1096 ladish_client_get_uuid(jclient, jclient_uuid);
1097 is_a2j = uuid_compare(jclient_uuid, g_a2j_uuid) == 0;
1098 is_empty = ladish_graph_client_is_empty(jack_graph, jclient);
1100 vgraph = ladish_client_get_vgraph(jclient);
1101 if (vgraph == NULL)
1103 ASSERT_NO_PASS;
1104 return true;
1107 //ladish_graph_dump(vgraph);
1109 if (!ladish_graph_client_looks_empty(jack_graph, jclient) ||
1110 !ladish_graph_client_is_hidden(jack_graph, jclient))
1112 return false;
1115 if (is_a2j)
1117 /* The a2j jclient has no interlinked vclient */
1118 return true;
1121 if (!ladish_client_get_interlink(jclient, vclient_uuid))
1123 if (is_empty)
1125 log_info("jack client of app '%s' has no interlinked vgraph client and no ports", app_name);
1127 else
1129 log_error("jack client of app '%s' has no interlinked vgraph client", app_name);
1130 ASSERT_NO_PASS;
1132 return true;
1135 vclient = ladish_graph_find_client_by_uuid(vgraph, vclient_uuid);
1136 if (vclient == NULL)
1138 ASSERT_NO_PASS;
1139 return true;
1142 if (!ladish_graph_client_looks_empty(vgraph, vclient))
1144 return false;
1147 ASSERT(ladish_graph_client_is_hidden(vgraph, vclient)); /* vclients are automatically hidden when they start looking empty (on port disappear) */
1148 return true;
1151 void
1152 ladish_virtualizer_remove_app(
1153 ladish_graph_handle jack_graph,
1154 const uuid_t app_uuid,
1155 const char * app_name)
1157 ladish_client_handle jclient;
1158 ladish_graph_handle vgraph;
1159 uuid_t vclient_uuid;
1160 ladish_client_handle vclient;
1161 bool is_empty;
1162 uuid_t jclient_uuid;
1163 bool is_a2j;
1165 //ladish_graph_dump(g_studio.jack_graph);
1167 jclient = ladish_graph_find_client_by_app(jack_graph, app_uuid);
1168 if (jclient == NULL)
1170 log_info("removing app without JACK client");
1171 return;
1174 ladish_client_get_uuid(jclient, jclient_uuid);
1175 is_a2j = uuid_compare(jclient_uuid, g_a2j_uuid) == 0;
1176 is_empty = ladish_graph_client_is_empty(jack_graph, jclient);
1178 vgraph = ladish_client_get_vgraph(jclient);
1179 if (vgraph == NULL)
1181 ASSERT_NO_PASS;
1182 return;
1185 //ladish_graph_dump(vgraph);
1187 ladish_graph_remove_client(jack_graph, jclient);
1189 if (is_a2j)
1191 /* The a2j jclient has no interlinked vclient */
1192 return;
1195 if (!ladish_client_get_interlink(jclient, vclient_uuid))
1197 if (is_empty)
1199 /* jack client without ports and thus without vgraph client */
1200 return;
1203 log_error("jack client of app '%s' has no interlinked vgraph client", app_name);
1204 ladish_graph_dump(g_studio.jack_graph);
1205 ladish_graph_dump(vgraph);
1206 ASSERT_NO_PASS;
1207 return;
1210 vclient = ladish_graph_find_client_by_uuid(vgraph, vclient_uuid);
1211 if (vclient == NULL)
1213 ASSERT_NO_PASS;
1214 return;
1217 ladish_graph_remove_client(vgraph, vclient);
1218 ladish_graph_dump(g_studio.jack_graph);
1219 ladish_graph_dump(vgraph);
1222 void
1223 ladish_virtualizer_destroy(
1224 ladish_virtualizer_handle handle)
1226 log_info("ladish_virtualizer_destroy() called");
1228 graph_proxy_detach((graph_proxy_handle)handle, virtualizer_ptr);
1229 free(virtualizer_ptr);
1232 #undef virtualizer_ptr