ladishd: properly detect connect failures
[ladish.git] / daemon / virtualizer.c
blobb9b50c956965c8c900c876a156ed4a4d1fcc3e89
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 "../common/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 ladish_graph_handle graph;
59 ladish_app_handle app;
62 #define app_find_context_ptr ((struct app_find_context *)context)
64 static bool lookup_app_in_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
66 pid_t pid;
67 ladish_app_handle app;
69 /* we stop iteration when app is found */
70 ASSERT(app_find_context_ptr->app == NULL && app_find_context_ptr->graph == NULL);
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 = app;
97 app_find_context_ptr->graph = graph;
99 return false; /* stop app supervisor iteration */
102 #undef app_find_context_ptr
104 static ladish_app_handle ladish_virtualizer_find_app_by_pid(struct virtualizer * virtualizer_ptr, pid_t pid, ladish_graph_handle * graph_ptr)
106 struct app_find_context context;
108 context.pid = pid;
109 context.app = NULL;
110 context.graph = NULL;
112 ladish_studio_iterate_virtual_graphs(&context, lookup_app_in_supervisor);
114 if (context.app == NULL)
115 { /* app not found */
116 ASSERT(context.graph == NULL);
117 return NULL;
120 ASSERT(context.graph != NULL);
121 *graph_ptr = context.graph;
123 return context.app;
126 struct find_link_port_context
128 uuid_t uuid;
129 uint64_t jack_id;
130 ladish_port_handle port;
131 ladish_graph_handle graph;
134 #define find_link_port_context_ptr ((struct find_link_port_context *)context)
136 static bool find_link_port_vgraph_callback_by_uuid(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
138 ladish_port_handle port;
140 port = ladish_graph_find_port_by_uuid(graph, find_link_port_context_ptr->uuid, true);
141 if (port != NULL)
143 find_link_port_context_ptr->port = port;
144 find_link_port_context_ptr->graph = graph;
145 return false;
148 return true; /* continue vgraph iteration */
151 static bool find_link_port_vgraph_callback_by_jack_id(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
153 ladish_port_handle port;
154 bool room;
156 log_info("searching link port with jack id %"PRIu64" in graph %s", find_link_port_context_ptr->jack_id, ladish_graph_get_description(graph));
158 room = graph != g_studio.studio_graph;
160 port = ladish_graph_find_port_by_jack_id(graph, find_link_port_context_ptr->jack_id, room, !room);
161 if (port != NULL)
163 find_link_port_context_ptr->port = port;
164 find_link_port_context_ptr->graph = graph;
165 return false;
168 return true; /* continue vgraph iteration */
171 #undef find_link_port_context_ptr
173 static ladish_graph_handle find_link_port_vgraph_by_uuid(struct virtualizer * virtualizer_ptr, const char * port_name, ladish_port_handle * port_ptr)
175 struct find_link_port_context context;
177 uuid_parse(port_name, context.uuid);
178 context.graph = NULL;
179 context.port = NULL;
181 ladish_studio_iterate_virtual_graphs(&context, find_link_port_vgraph_callback_by_uuid);
183 if (port_ptr != NULL && context.graph != NULL)
185 *port_ptr = context.port;
188 return context.graph;
191 static ladish_graph_handle find_link_port_vgraph_by_jack_id(struct virtualizer * virtualizer_ptr, uint64_t jack_id, ladish_port_handle * port_ptr)
193 struct find_link_port_context context;
195 context.jack_id = jack_id;
196 context.graph = NULL;
197 context.port = NULL;
199 ladish_studio_iterate_virtual_graphs(&context, find_link_port_vgraph_callback_by_jack_id);
201 if (port_ptr != NULL && context.graph != NULL)
203 *port_ptr = context.port;
206 return context.graph;
209 static
210 bool
211 lookup_port(
212 struct virtualizer * virtualizer_ptr,
213 uint64_t port_id,
214 ladish_port_handle * port_ptr,
215 ladish_graph_handle * vgraph_ptr)
217 ladish_port_handle port;
218 ladish_client_handle jclient;
219 ladish_graph_handle vgraph;
221 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
222 if (port == NULL)
224 log_error("Unknown JACK port with id %"PRIu64" (dis)connected", port_id);
225 return false;
228 jclient = ladish_graph_get_port_client(virtualizer_ptr->jack_graph, port);
229 if (jclient == NULL)
231 log_error("Port %"PRIu64" without jack client was (dis)connected", port_id);
232 return false;
235 vgraph = ladish_client_get_vgraph(jclient);
236 if (vgraph == NULL)
238 vgraph = find_link_port_vgraph_by_jack_id(virtualizer_ptr, port_id, NULL);
239 if (vgraph == NULL)
241 log_error("Cannot find vgraph for (dis)connected jmcore port");
242 return false;
245 log_info("link port found in graph %s", ladish_graph_get_description(vgraph));
248 *port_ptr = port;
249 *vgraph_ptr = vgraph;
250 return true;
253 #define virtualizer_ptr ((struct virtualizer *)context)
255 static void clear(void * context)
257 log_info("clear");
260 static void client_appeared(void * context, uint64_t id, const char * jack_name)
262 ladish_client_handle client;
263 const char * a2j_name;
264 bool is_a2j;
265 ladish_app_handle app;
266 uuid_t app_uuid;
267 const char * name;
268 pid_t pid;
269 ladish_graph_handle graph;
270 bool jmcore;
272 log_info("client_appeared(%"PRIu64", %s)", id, jack_name);
274 a2j_name = a2j_proxy_get_jack_client_name_cached();
275 is_a2j = a2j_name != NULL && strcmp(a2j_name, jack_name) == 0;
277 name = jack_name;
278 app = NULL;
279 graph = NULL;
280 jmcore = false;
282 if (!graph_proxy_get_client_pid(virtualizer_ptr->jack_graph_proxy, id, &pid))
284 log_info("client %"PRIu64" pid is unknown", id);
286 else
288 log_info("client pid is %"PRId64, (int64_t)pid);
290 if (pid != 0) /* skip internal clients that will match the pending clients in the graph, both have zero pid */
292 jmcore = pid == jmcore_proxy_get_pid_cached();
293 if (jmcore)
295 log_info("jmcore client appeared");
297 else
299 app = ladish_virtualizer_find_app_by_pid(virtualizer_ptr, pid, &graph);
300 if (app != NULL)
302 ladish_app_get_uuid(app, app_uuid);
303 ASSERT(!uuid_is_null(app_uuid));
304 name = ladish_app_get_name(app);
305 log_info("app name is '%s'", name);
311 if (!jmcore)
313 if (is_a2j)
315 client = ladish_graph_find_client_by_uuid(virtualizer_ptr->jack_graph, g_a2j_uuid);
317 else
319 client = ladish_graph_find_client_by_app(virtualizer_ptr->jack_graph, app_uuid);
320 if (client == NULL)
322 log_info("Lookup by app uuid failed, attempting lookup by name '%s'", name);
323 client = ladish_graph_find_client_by_name(virtualizer_ptr->jack_graph, name, true);
327 if (client != NULL)
329 log_info("found existing client");
330 if (ladish_client_get_jack_id(client) != 0)
332 log_error("Ignoring client with duplicate name '%s' ('%s')", name, jack_name);
333 goto exit;
336 ladish_client_set_jack_id(client, id);
337 ladish_graph_show_client(virtualizer_ptr->jack_graph, client);
338 goto done;
342 if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, &client))
344 log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, jack_name);
345 goto exit;
348 ladish_client_set_jack_id(client, id);
350 if (!ladish_graph_add_client(virtualizer_ptr->jack_graph, client, name, false))
352 log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
353 ladish_client_destroy(client);
354 goto exit;
357 done:
358 if (strcmp(jack_name, "system") == 0)
360 virtualizer_ptr->system_client_id = id;
363 if (app != NULL)
365 /* interlink client and app */
366 ladish_app_add_pid(app, pid);
367 ladish_client_set_pid(client, pid);
368 ladish_client_set_app(client, app_uuid);
370 ASSERT(graph);
371 ladish_client_set_vgraph(client, graph);
372 virtualizer_ptr->our_clients_count++;
374 else if (jmcore)
376 ladish_client_set_pid(client, pid);
377 ASSERT(ladish_client_get_vgraph(client) == NULL);
379 else
381 /* unknown and internal clients appear in the studio graph */
382 ladish_client_set_vgraph(client, g_studio.studio_graph);
385 exit:
386 return;
389 static void client_disappeared(void * context, uint64_t id)
391 ladish_client_handle client;
392 pid_t pid;
393 ladish_graph_handle vgraph;
395 log_info("client_disappeared(%"PRIu64")", id);
397 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, id);
398 if (client == NULL)
400 log_error("Unknown JACK client with id %"PRIu64" disappeared", id);
401 return;
404 log_info("client disappeared: '%s'", ladish_graph_get_client_name(virtualizer_ptr->jack_graph, client));
406 vgraph = ladish_client_get_vgraph(client);
408 pid = ladish_client_get_pid(client);
409 if (pid != 0 && pid != jmcore_proxy_get_pid_cached())
411 virtualizer_ptr->our_clients_count--;
414 if (id == virtualizer_ptr->system_client_id)
416 virtualizer_ptr->system_client_id = 0;
419 if (vgraph != NULL && ladish_graph_is_persist(vgraph)) /* if client is supposed to be persisted */
421 ladish_client_set_jack_id(client, 0);
422 ladish_graph_hide_client(virtualizer_ptr->jack_graph, client);
424 else
426 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client);
427 ladish_client_destroy(client);
428 /* no need to clear vclient interlink because it either does not exist (vgraph is NULL) or
429 * it will be destroyed before it is accessed (persist flag is cleared on room deletion) */
433 static
434 void
435 port_appeared(
436 void * context,
437 uint64_t client_id,
438 uint64_t port_id,
439 const char * real_jack_port_name,
440 bool is_input,
441 bool is_terminal,
442 bool is_midi)
444 ladish_client_handle jack_client;
445 ladish_client_handle vclient;
446 ladish_port_handle port;
447 uint32_t type;
448 uint32_t flags;
449 const char * jack_client_name;
450 bool is_a2j;
451 uuid_t jclient_uuid;
452 uuid_t vclient_uuid;
453 bool has_app;
454 uuid_t app_uuid;
455 char * alsa_client_name;
456 char * alsa_port_name;
457 char * a2j_fake_jack_port_name = NULL;
458 uint32_t alsa_client_id;
459 const char * jack_port_name;
460 const char * vport_name;
461 ladish_graph_handle vgraph;
463 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");
465 type = is_midi ? JACKDBUS_PORT_TYPE_MIDI : JACKDBUS_PORT_TYPE_AUDIO;
466 flags = is_input ? JACKDBUS_PORT_FLAG_INPUT : JACKDBUS_PORT_FLAG_OUTPUT;
467 if (is_terminal)
469 flags |= JACKDBUS_PORT_FLAG_TERMINAL;
472 /********************/
473 /* gather info about the appeared port */
475 jack_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
476 if (jack_client == NULL)
478 log_error("Port of unknown JACK client with id %"PRIu64" appeared", client_id);
479 goto exit;
482 has_app = ladish_client_get_app(jack_client, app_uuid);
484 /* find the virtual graph that owns the app that owns the client that owns the appeared port */
485 vgraph = ladish_client_get_vgraph(jack_client);
486 if (vgraph == NULL)
488 vgraph = find_link_port_vgraph_by_uuid(virtualizer_ptr, real_jack_port_name, &port);
489 if (vgraph == NULL)
491 log_error("Cannot find vgraph for appeared jmcore port '%s'", real_jack_port_name);
492 goto exit;
495 /* jmcore port appeared */
497 log_info("jmcore port appeared in vgraph %s", ladish_graph_get_description(vgraph));
499 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, real_jack_port_name, type, flags, false))
501 log_error("ladish_graph_add_port() failed.");
502 goto exit;
505 if (vgraph == g_studio.studio_graph)
507 ladish_port_set_jack_id(port, port_id);
509 else
511 ladish_port_set_jack_id_room(port, port_id);
514 vclient = ladish_graph_get_port_client(vgraph, port);
515 if (vclient == NULL)
517 log_error("link port client not found in vgraph %s", ladish_graph_get_description(vgraph));
518 ASSERT_NO_PASS;
519 goto exit;
522 ladish_graph_show_port(vgraph, port);
523 goto exit;
526 jack_client_name = ladish_graph_get_client_name(virtualizer_ptr->jack_graph, jack_client);
528 ladish_client_get_uuid(jack_client, jclient_uuid);
529 is_a2j = uuid_compare(jclient_uuid, g_a2j_uuid) == 0;
530 if (is_a2j)
532 log_info("a2j port appeared");
533 if (!a2j_proxy_map_jack_port(real_jack_port_name, &alsa_client_name, &alsa_port_name, &alsa_client_id))
535 is_a2j = false;
536 alsa_client_name = catdup("FAILED ", jack_client_name);
537 if (alsa_client_name == NULL)
539 log_error("catdup failed to duplicate a2j jack client name after map failure");
540 goto exit;
543 alsa_port_name = strdup(real_jack_port_name);
544 if (alsa_port_name == NULL)
546 log_error("catdup failed to duplicate a2j jack port name after map failure");
547 free(alsa_client_name);
548 goto exit;
551 else
553 log_info("a2j: '%s':'%s' (%"PRIu32")", alsa_client_name, alsa_port_name, alsa_client_id);
556 a2j_fake_jack_port_name = catdup4(alsa_client_name, is_input ? " (playback)" : " (capture)", ": ", alsa_port_name);
557 if (a2j_fake_jack_port_name == NULL)
559 log_error("catdup4() failed");
560 goto free_alsa_names;
563 jack_port_name = a2j_fake_jack_port_name;
565 else
567 jack_port_name = real_jack_port_name;
570 /********************/
572 /* search (by name) the appeared port in jack graph
573 * if found - show it in both graphs.
574 * if not found - create new port and add it to the jack graph.
575 * Then process to adding it to virtual graph */
577 port = ladish_graph_find_port_by_name(virtualizer_ptr->jack_graph, jack_client, jack_port_name);
578 if (port != NULL)
580 log_info("found existing port");
582 if (ladish_port_get_jack_id(port) != 0)
584 log_error("Ignoring duplicate JACK port '%s':'%s'", jack_client_name, jack_port_name);
585 goto free_alsa_names;
588 ladish_port_set_jack_id(port, port_id);
589 ladish_graph_adjust_port(virtualizer_ptr->jack_graph, port, type, flags);
590 ladish_graph_show_port(virtualizer_ptr->jack_graph, port);
592 vclient = ladish_graph_get_port_client(vgraph, port);
593 if (vclient == NULL)
595 log_error("JACK port not found in virtual graph");
596 ASSERT_NO_PASS;
597 goto free_alsa_names;
600 ladish_client_set_jack_id(vclient, client_id);
601 ladish_graph_adjust_port(vgraph, port, type, flags);
602 ladish_graph_show_port(vgraph, port);
603 goto free_alsa_names;
606 if (!ladish_port_create(NULL, false, &port))
608 log_error("ladish_port_create() failed.");
609 goto free_alsa_names;
612 /* set port jack id so invisible connections to/from it can be restored */
613 ladish_port_set_jack_id(port, port_id);
615 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, jack_port_name, type, flags, false))
617 log_error("ladish_graph_add_port() failed.");
618 ladish_port_destroy(port);
619 goto free_alsa_names;
622 /********************/
623 /* find/create the virtual client where port will be added */
625 if (is_a2j)
627 vclient = ladish_graph_find_client_by_name(vgraph, alsa_client_name, false);
628 if (vclient == NULL)
630 if (!ladish_client_create(NULL, &vclient))
632 log_error("ladish_client_create() failed.");
633 goto free_alsa_names;
636 if (!ladish_graph_add_client(vgraph, vclient, alsa_client_name, false))
638 log_error("ladish_graph_add_client() failed.");
639 ladish_client_destroy(vclient);
640 goto free_alsa_names;
644 else if (client_id == virtualizer_ptr->system_client_id)
646 log_info("system client port appeared");
648 if (!is_input)
649 { /* output capture port */
651 vclient = ladish_graph_find_client_by_uuid(vgraph, g_system_capture_uuid);
652 if (vclient == NULL)
654 if (!ladish_client_create(g_system_capture_uuid, &vclient))
656 log_error("ladish_client_create() failed.");
657 goto free_alsa_names;
660 if (!ladish_graph_add_client(vgraph, vclient, "Hardware Capture", false))
662 log_error("ladish_graph_add_client() failed.");
663 ladish_client_destroy(vclient);
664 goto free_alsa_names;
668 else
669 { /* input playback port */
670 vclient = ladish_graph_find_client_by_uuid(vgraph, g_system_playback_uuid);
671 if (vclient == NULL)
673 if (!ladish_client_create(g_system_playback_uuid, &vclient))
675 log_error("ladish_client_create() failed.");
676 goto free_alsa_names;
679 if (!ladish_graph_add_client(vgraph, vclient, "Hardware Playback", false))
681 ladish_client_destroy(vclient);
682 goto free_alsa_names;
687 else
688 { /* non-system client */
689 log_info("non-system client port appeared");
691 if (ladish_client_get_interlink(jack_client, vclient_uuid))
693 vclient = ladish_graph_find_client_by_uuid(vgraph, vclient_uuid);
694 ASSERT(vclient != NULL);
696 else
698 log_info("creating new vclient");
699 if (!ladish_client_create(NULL, &vclient))
701 log_error("ladish_client_create() failed.");
702 goto free_alsa_names;
705 ladish_client_interlink(vclient, jack_client);
707 if (has_app)
709 ladish_client_set_app(vclient, app_uuid);
712 if (!ladish_graph_add_client(vgraph, vclient, jack_client_name, false))
714 log_error("ladish_graph_add_client() failed to add client '%s' to virtual graph", jack_client_name);
715 ladish_client_destroy(vclient);
716 goto free_alsa_names;
721 /********************/
722 /* add newly appeared port to the virtual graph */
724 if (is_a2j)
726 vport_name = alsa_port_name;
728 else
730 vport_name = jack_port_name;
733 if (!ladish_graph_add_port(vgraph, vclient, port, vport_name, type, flags, false))
735 log_error("ladish_graph_add_port() failed.");
736 goto free_alsa_names;
739 free_alsa_names:
740 if (a2j_fake_jack_port_name != NULL)
742 free(a2j_fake_jack_port_name);
745 if (is_a2j)
747 free(alsa_client_name);
748 free(alsa_port_name);
751 exit:
752 return;
755 static void port_disappeared(void * context, uint64_t client_id, uint64_t port_id)
757 ladish_client_handle jclient;
758 ladish_client_handle vclient;
759 ladish_port_handle port;
760 ladish_graph_handle vgraph;
761 bool jmcore;
763 log_info("port_disappeared(%"PRIu64", %"PRIu64")", client_id, port_id);
765 jclient = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
766 if (jclient == NULL)
768 log_error("Port of unknown JACK client with id %"PRIu64" disappeared", client_id);
769 return;
772 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
773 if (port == NULL)
775 log_error("Unknown JACK port with id %"PRIu64" disappeared", port_id);
776 return;
779 /* find the virtual graph that owns the app that owns the client that owns the disappeared port */
780 jmcore = false;
781 vgraph = ladish_client_get_vgraph(jclient);
782 if (vgraph == NULL)
784 vgraph = find_link_port_vgraph_by_uuid(virtualizer_ptr, ladish_graph_get_port_name(virtualizer_ptr->jack_graph, port), NULL);
785 if (vgraph == NULL)
787 log_error("Cannot find vgraph for disappeared jmcore port");
788 ASSERT_NO_PASS;
789 return;
792 jmcore = true;
793 ladish_graph_remove_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
796 if (ladish_graph_is_persist(vgraph)) /* if port is supposed to be persisted */
798 if (!jmcore)
800 ladish_port_set_jack_id(port, 0);
801 ladish_graph_hide_port(virtualizer_ptr->jack_graph, port);
803 if (vgraph != NULL)
805 ladish_graph_hide_port(vgraph, port);
806 vclient = ladish_graph_get_port_client(vgraph, port);
807 if (ladish_graph_client_looks_empty(vgraph, vclient))
809 ladish_graph_hide_client(vgraph, vclient);
813 else
815 if (!jmcore)
817 ladish_graph_remove_port(virtualizer_ptr->jack_graph, port);
820 if (vgraph != NULL)
822 vclient = ladish_graph_remove_port(vgraph, port);
823 if (vclient != NULL)
825 if (ladish_graph_client_is_empty(vgraph, vclient))
827 ladish_graph_remove_client(vgraph, vclient);
828 ladish_client_clear_interlink(jclient);
835 static void port_renamed(void * context, uint64_t client_id, uint64_t port_id, const char * old_port_name, const char * new_port_name)
837 ladish_client_handle client;
838 ladish_port_handle port;
839 ladish_graph_handle vgraph;
841 log_info("port_renamed(%"PRIu64", '%s', '%s')", port_id, old_port_name, new_port_name);
843 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
844 if (client == NULL)
846 log_error("Port of unknown JACK client with id %"PRIu64" was renamed", client_id);
847 return;
850 /* find the virtual graph that owns the app that owns the client that owns the renamed port */
851 vgraph = ladish_client_get_vgraph(client);
853 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
854 if (port == NULL)
856 log_error("Unknown JACK port with id %"PRIu64" was renamed", port_id);
857 return;
860 if (!ladish_graph_rename_port(virtualizer_ptr->jack_graph, port, new_port_name))
862 log_error("renaming of port in jack graph failed");
865 if (!ladish_graph_rename_port(vgraph, port, new_port_name))
867 log_error("renaming of port in virtual graph failed");
871 static bool ports_connect_request(void * context, ladish_graph_handle graph_handle, ladish_port_handle port1, ladish_port_handle port2)
873 uint64_t port1_id;
874 uint64_t port2_id;
876 ASSERT(ladish_graph_get_opath(graph_handle)); /* studio or room virtual graph */
877 log_info("virtualizer: ports connect request");
879 if (graph_handle == g_studio.studio_graph)
881 port1_id = ladish_port_get_jack_id(port1);
882 port2_id = ladish_port_get_jack_id(port2);
884 else
886 port1_id = ladish_port_get_jack_id_room(port1);
887 port2_id = ladish_port_get_jack_id_room(port2);
890 return graph_proxy_connect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
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 return graph_proxy_disconnect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
924 static void ports_connected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
926 ladish_port_handle port1;
927 ladish_port_handle port2;
928 uint64_t connection_id;
929 ladish_graph_handle vgraph1;
930 ladish_graph_handle vgraph2;
932 log_info("ports_connected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
934 if (!lookup_port(virtualizer_ptr, port1_id, &port1, &vgraph1))
936 return;
939 if (!lookup_port(virtualizer_ptr, port2_id, &port2, &vgraph2))
941 return;
944 if (vgraph1 != vgraph2)
946 /* TODO */
947 log_error("ignoring connection with endpoints in different vgraphs");
948 return;
951 ladish_graph_add_connection(virtualizer_ptr->jack_graph, port1, port2, false);
953 if (ladish_graph_find_connection(vgraph1, port1, port2, &connection_id))
955 log_info("showing hidden virtual connection");
956 ladish_graph_show_connection(vgraph1, connection_id);
958 else
960 log_info("creating new virtual connection");
961 ladish_graph_add_connection(vgraph1, port1, port2, false);
965 static void ports_disconnected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
967 ladish_port_handle port1;
968 ladish_port_handle port2;
969 uint64_t connection_id;
970 ladish_graph_handle vgraph1;
971 ladish_graph_handle vgraph2;
973 log_info("ports_disconnected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
975 if (!lookup_port(virtualizer_ptr, port1_id, &port1, &vgraph1))
977 return;
980 if (!lookup_port(virtualizer_ptr, port2_id, &port2, &vgraph2))
982 return;
985 if (vgraph1 != vgraph2)
987 /* TODO */
988 log_error("ignoring connection with endpoints in different vgraphs");
989 return;
992 if (ladish_graph_find_connection(virtualizer_ptr->jack_graph, port1, port2, &connection_id))
994 ladish_graph_remove_connection(virtualizer_ptr->jack_graph, connection_id, true);
996 else
998 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the JACK graph", client1_id, port1_id, client2_id, port2_id);
1001 if (ladish_graph_find_connection(vgraph1, port1, port2, &connection_id))
1003 ladish_graph_remove_connection(vgraph1, connection_id, false);
1005 else
1007 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the virtual graph", client1_id, port1_id, client2_id, port2_id);
1011 #undef virtualizer_ptr
1013 bool
1014 ladish_virtualizer_create(
1015 graph_proxy_handle jack_graph_proxy,
1016 ladish_graph_handle jack_graph,
1017 ladish_virtualizer_handle * handle_ptr)
1019 struct virtualizer * virtualizer_ptr;
1021 virtualizer_ptr = malloc(sizeof(struct virtualizer));
1022 if (virtualizer_ptr == NULL)
1024 log_error("malloc() failed for struct virtualizer");
1025 return false;
1028 virtualizer_ptr->jack_graph_proxy = jack_graph_proxy;
1029 virtualizer_ptr->jack_graph = jack_graph;
1030 virtualizer_ptr->system_client_id = 0;
1031 virtualizer_ptr->our_clients_count = 0;
1033 if (!graph_proxy_attach(
1034 jack_graph_proxy,
1035 virtualizer_ptr,
1036 clear,
1037 client_appeared,
1038 NULL, /* jackdbus does not have client rename functionality (yet) */
1039 client_disappeared,
1040 port_appeared,
1041 port_renamed,
1042 port_disappeared,
1043 ports_connected,
1044 ports_disconnected))
1046 free(virtualizer_ptr);
1047 return false;
1050 *handle_ptr = (ladish_virtualizer_handle)virtualizer_ptr;
1051 return true;
1054 #define virtualizer_ptr ((struct virtualizer *)handle)
1056 void
1057 ladish_virtualizer_set_graph_connection_handlers(
1058 ladish_virtualizer_handle handle,
1059 ladish_graph_handle graph)
1061 ladish_graph_set_connection_handlers(graph, virtualizer_ptr, ports_connect_request, ports_disconnect_request);
1064 unsigned int
1065 ladish_virtualizer_get_our_clients_count(
1066 ladish_virtualizer_handle handle)
1068 return virtualizer_ptr->our_clients_count;
1071 bool
1072 ladish_virtualizer_is_hidden_app(
1073 ladish_graph_handle jack_graph,
1074 const uuid_t app_uuid,
1075 const char * app_name)
1077 ladish_client_handle jclient;
1078 ladish_graph_handle vgraph;
1079 uuid_t vclient_uuid;
1080 ladish_client_handle vclient;
1081 bool is_empty;
1082 uuid_t jclient_uuid;
1083 bool is_a2j;
1085 //ladish_graph_dump(g_studio.jack_graph);
1087 jclient = ladish_graph_find_client_by_app(jack_graph, app_uuid);
1088 if (jclient == NULL)
1090 log_info("App without JACK client is treated as hidden one");
1091 return true;
1094 ladish_client_get_uuid(jclient, jclient_uuid);
1095 is_a2j = uuid_compare(jclient_uuid, g_a2j_uuid) == 0;
1096 is_empty = ladish_graph_client_is_empty(jack_graph, jclient);
1098 vgraph = ladish_client_get_vgraph(jclient);
1099 if (vgraph == NULL)
1101 ASSERT_NO_PASS;
1102 return true;
1105 //ladish_graph_dump(vgraph);
1107 if (!ladish_graph_client_looks_empty(jack_graph, jclient) ||
1108 !ladish_graph_client_is_hidden(jack_graph, jclient))
1110 return false;
1113 if (is_a2j)
1115 /* The a2j jclient has no interlinked vclient */
1116 return true;
1119 if (!ladish_client_get_interlink(jclient, vclient_uuid))
1121 if (is_empty)
1123 log_info("jack client of app '%s' has no interlinked vgraph client and no ports", app_name);
1125 else
1127 log_error("jack client of app '%s' has no interlinked vgraph client", app_name);
1128 ASSERT_NO_PASS;
1130 return true;
1133 vclient = ladish_graph_find_client_by_uuid(vgraph, vclient_uuid);
1134 if (vclient == NULL)
1136 ASSERT_NO_PASS;
1137 return true;
1140 if (!ladish_graph_client_looks_empty(vgraph, vclient))
1142 return false;
1145 ASSERT(ladish_graph_client_is_hidden(vgraph, vclient)); /* vclients are automatically hidden when they start looking empty (on port disappear) */
1146 return true;
1149 void
1150 ladish_virtualizer_remove_app(
1151 ladish_graph_handle jack_graph,
1152 const uuid_t app_uuid,
1153 const char * app_name)
1155 ladish_client_handle jclient;
1156 ladish_graph_handle vgraph;
1157 uuid_t vclient_uuid;
1158 ladish_client_handle vclient;
1159 bool is_empty;
1160 uuid_t jclient_uuid;
1161 bool is_a2j;
1163 //ladish_graph_dump(g_studio.jack_graph);
1165 jclient = ladish_graph_find_client_by_app(jack_graph, app_uuid);
1166 if (jclient == NULL)
1168 log_info("removing app without JACK client");
1169 return;
1172 ladish_client_get_uuid(jclient, jclient_uuid);
1173 is_a2j = uuid_compare(jclient_uuid, g_a2j_uuid) == 0;
1174 is_empty = ladish_graph_client_is_empty(jack_graph, jclient);
1176 vgraph = ladish_client_get_vgraph(jclient);
1177 if (vgraph == NULL)
1179 ASSERT_NO_PASS;
1180 return;
1183 //ladish_graph_dump(vgraph);
1185 ladish_graph_remove_client(jack_graph, jclient);
1187 if (is_a2j)
1189 /* The a2j jclient has no interlinked vclient */
1190 return;
1193 if (!ladish_client_get_interlink(jclient, vclient_uuid))
1195 if (is_empty)
1197 /* jack client without ports and thus without vgraph client */
1198 return;
1201 log_error("jack client of app '%s' has no interlinked vgraph client", app_name);
1202 ladish_graph_dump(g_studio.jack_graph);
1203 ladish_graph_dump(vgraph);
1204 ASSERT_NO_PASS;
1205 return;
1208 vclient = ladish_graph_find_client_by_uuid(vgraph, vclient_uuid);
1209 if (vclient == NULL)
1211 ASSERT_NO_PASS;
1212 return;
1215 ladish_graph_remove_client(vgraph, vclient);
1216 ladish_graph_dump(g_studio.jack_graph);
1217 ladish_graph_dump(vgraph);
1220 void
1221 ladish_virtualizer_destroy(
1222 ladish_virtualizer_handle handle)
1224 log_info("ladish_virtualizer_destroy() called");
1226 graph_proxy_detach((graph_proxy_handle)handle, virtualizer_ptr);
1227 free(virtualizer_ptr);
1230 #undef virtualizer_ptr
1232 #define vgraph ((ladish_graph_handle)vgraph_context)
1233 void
1234 ladish_virtualizer_rename_app(
1235 void * vgraph_context,
1236 const uuid_t uuid,
1237 const char * old_name,
1238 const char * new_app_name)
1240 ladish_client_handle client;
1242 client = ladish_graph_find_client_by_app(vgraph, uuid);
1243 if (client != NULL)
1245 ladish_graph_rename_client(vgraph, client, new_app_name);
1248 client = ladish_graph_find_client_by_app(g_studio.jack_graph, uuid);
1249 if (client != NULL)
1251 ladish_graph_rename_client(g_studio.jack_graph, client, new_app_name);
1254 #undef vgraph
1256 bool
1257 ladish_virtualizer_is_system_client(
1258 uuid_t uuid)
1260 if (uuid_compare(uuid, g_system_capture_uuid) == 0)
1262 return true;
1265 if (uuid_compare(uuid, g_system_playback_uuid) == 0)
1267 return true;
1270 return false;