daemon: Fix invalid memory access
[ladish.git] / daemon / virtualizer.c
blob14a89274c8c5f09c1f9b43fbec6225665baa961a
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 ladish_graph_handle graph;
62 #define app_find_context_ptr ((struct app_find_context *)context)
64 bool get_app_name_from_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
66 pid_t pid;
67 char * app_name;
69 ASSERT(app_find_context_ptr->app_name == NULL); /* we stop iteration when app is found */
71 //log_info("checking app supervisor \"%s\" for pid %llu", ladish_app_supervisor_get_name(app_supervisor), (unsigned long long)pid);
73 pid = app_find_context_ptr->pid;
76 app_name = ladish_app_supervisor_search_app(app_supervisor, pid);
77 if (app_name != NULL)
78 break;
80 pid = (pid_t)procfs_get_process_parent((unsigned long long)pid);
81 #if 0
82 if (pid != 0)
84 log_info("parent pid %llu", (unsigned long long)pid);
86 #endif
88 while (pid != 0);
90 if (app_name != NULL)
92 app_find_context_ptr->app_name = app_name;
93 app_find_context_ptr->pid = pid;
94 app_find_context_ptr->graph = graph;
95 return false; /* stop app supervisor iteration */
98 return true; /* continue app supervisor iteration */
101 #undef app_find_context_ptr
103 char * get_app_name(struct virtualizer * virtualizer_ptr, uint64_t client_id, pid_t pid, ladish_graph_handle * graph_ptr)
105 struct app_find_context context;
107 context.pid = (pid_t)pid;
108 context.app_name = NULL;
109 context.graph = NULL;
111 studio_iterate_virtual_graphs(&context, get_app_name_from_supervisor);
113 if (context.app_name != NULL)
115 ASSERT(context.graph != NULL);
116 *graph_ptr = context.graph;
119 return context.app_name;
122 struct find_link_port_context
124 uuid_t uuid;
125 uint64_t jack_id;
126 ladish_port_handle port;
127 ladish_graph_handle graph;
130 #define find_link_port_context_ptr ((struct find_link_port_context *)context)
132 static bool find_link_port_vgraph_callback_by_uuid(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
134 ladish_port_handle port;
136 port = ladish_graph_find_port_by_uuid(graph, find_link_port_context_ptr->uuid, true);
137 if (port != NULL)
139 find_link_port_context_ptr->port = port;
140 find_link_port_context_ptr->graph = graph;
141 return false;
144 return true; /* continue vgraph iteration */
147 static bool find_link_port_vgraph_callback_by_jack_id(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
149 ladish_port_handle port;
150 bool room;
152 log_info("searching link port with jack id %"PRIu64" in graph %s", find_link_port_context_ptr->jack_id, ladish_graph_get_description(graph));
154 room = graph != g_studio.studio_graph;
156 port = ladish_graph_find_port_by_jack_id(graph, find_link_port_context_ptr->jack_id, room, !room);
157 if (port != NULL)
159 find_link_port_context_ptr->port = port;
160 find_link_port_context_ptr->graph = graph;
161 return false;
164 return true; /* continue vgraph iteration */
167 #undef find_link_port_context_ptr
169 static ladish_graph_handle find_link_port_vgraph_by_uuid(struct virtualizer * virtualizer_ptr, const char * port_name, ladish_port_handle * port_ptr)
171 struct find_link_port_context context;
173 uuid_parse(port_name, context.uuid);
174 context.graph = NULL;
175 context.port = NULL;
177 studio_iterate_virtual_graphs(&context, find_link_port_vgraph_callback_by_uuid);
179 if (port_ptr != NULL && context.graph != NULL)
181 *port_ptr = context.port;
184 return context.graph;
187 static ladish_graph_handle find_link_port_vgraph_by_jack_id(struct virtualizer * virtualizer_ptr, uint64_t jack_id, ladish_port_handle * port_ptr)
189 struct find_link_port_context context;
191 context.jack_id = jack_id;
192 context.graph = NULL;
193 context.port = NULL;
195 studio_iterate_virtual_graphs(&context, find_link_port_vgraph_callback_by_jack_id);
197 if (port_ptr != NULL && context.graph != NULL)
199 *port_ptr = context.port;
202 return context.graph;
205 static
206 bool
207 lookup_port(
208 struct virtualizer * virtualizer_ptr,
209 uint64_t port_id,
210 ladish_port_handle * port_ptr,
211 ladish_graph_handle * vgraph_ptr)
213 ladish_port_handle port;
214 ladish_client_handle jclient;
215 ladish_graph_handle vgraph;
217 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
218 if (port == NULL)
220 log_error("Unknown JACK port with id %"PRIu64" (dis)connected", port_id);
221 return false;
224 jclient = ladish_graph_get_port_client(virtualizer_ptr->jack_graph, port);
225 if (jclient == NULL)
227 log_error("Port %"PRIu64" without jack client was (dis)connected", port_id);
228 return false;
231 vgraph = ladish_client_get_vgraph(jclient);
232 if (vgraph == NULL)
234 vgraph = find_link_port_vgraph_by_jack_id(virtualizer_ptr, port_id, NULL);
235 if (vgraph == NULL)
237 log_error("Cannot find vgraph for (dis)connected jmcore port");
238 return false;
241 log_info("link port found in graph %s", ladish_graph_get_description(vgraph));
244 *port_ptr = port;
245 *vgraph_ptr = vgraph;
246 return true;
249 #define virtualizer_ptr ((struct virtualizer *)context)
251 static void clear(void * context)
253 log_info("clear");
256 static void client_appeared(void * context, uint64_t id, const char * jack_name)
258 ladish_client_handle client;
259 const char * a2j_name;
260 bool is_a2j;
261 char * app_name;
262 const char * name;
263 pid_t pid;
264 ladish_graph_handle graph;
265 bool jmcore;
267 log_info("client_appeared(%"PRIu64", %s)", id, jack_name);
269 a2j_name = a2j_proxy_get_jack_client_name_cached();
270 is_a2j = a2j_name != NULL && strcmp(a2j_name, jack_name) == 0;
272 name = jack_name;
273 app_name = NULL;
274 jmcore = false;
276 if (!graph_proxy_get_client_pid(virtualizer_ptr->jack_graph_proxy, id, &pid))
278 log_info("client %"PRIu64" pid is unknown", id);
280 else
282 log_info("client pid is %"PRId64, (int64_t)pid);
284 if (pid != 0) /* skip internal clients that will match the pending clients in the graph, both have zero pid */
286 jmcore = pid == jmcore_proxy_get_pid_cached();
287 if (jmcore)
289 log_info("jmcore client appeared");
291 else
293 app_name = get_app_name(virtualizer_ptr, id, pid, &graph);
294 if (app_name != NULL)
296 log_info("app name is '%s'", app_name);
297 name = app_name;
303 if (!jmcore)
305 if (is_a2j)
307 client = ladish_graph_find_client_by_uuid(virtualizer_ptr->jack_graph, g_a2j_uuid);
309 else
311 client = ladish_graph_find_client_by_name(virtualizer_ptr->jack_graph, name);
314 if (client != NULL)
316 log_info("found existing client");
317 if (ladish_client_get_jack_id(client) != 0)
319 log_error("Ignoring client with duplicate name '%s' ('%s')", name, jack_name);
320 goto free_app_name;
323 ladish_client_set_jack_id(client, id);
324 ladish_graph_show_client(virtualizer_ptr->jack_graph, client);
325 goto done;
329 if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, &client))
331 log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, jack_name);
332 goto free_app_name;
335 ladish_client_set_jack_id(client, id);
337 if (!ladish_graph_add_client(virtualizer_ptr->jack_graph, client, name, false))
339 log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
340 ladish_client_destroy(client);
341 goto free_app_name;
344 done:
345 if (strcmp(jack_name, "system") == 0)
347 virtualizer_ptr->system_client_id = id;
350 if (app_name != NULL)
352 ladish_client_set_pid(client, pid);
353 ladish_client_set_vgraph(client, graph);
354 virtualizer_ptr->our_clients_count++;
356 else if (jmcore)
358 ladish_client_set_pid(client, pid);
359 ASSERT(ladish_client_get_vgraph(client) == NULL);
361 else
363 /* unknown and internal clients appear in the studio graph */
364 ladish_client_set_vgraph(client, g_studio.studio_graph);
367 free_app_name:
368 if (app_name != NULL)
370 free(app_name);
374 static void client_disappeared(void * context, uint64_t id)
376 ladish_client_handle client;
377 pid_t pid;
379 log_info("client_disappeared(%"PRIu64")", id);
381 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, id);
382 if (client == NULL)
384 log_error("Unknown JACK client with id %"PRIu64" disappeared", id);
385 return;
388 log_info("client disappeared: '%s'", ladish_graph_get_client_name(virtualizer_ptr->jack_graph, client));
390 if (ladish_client_get_vgraph(client) == NULL)
391 { /* remove jmcore clients, the are not persisted in the jack graph */
392 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client);
393 ladish_client_destroy(client);
394 return;
397 pid = ladish_client_get_pid(client);
398 if (pid != 0)
400 virtualizer_ptr->our_clients_count--;
403 if (id == virtualizer_ptr->system_client_id)
405 virtualizer_ptr->system_client_id = 0;
408 if (true) /* if client is supposed to be persisted */
410 ladish_client_set_jack_id(client, 0);
411 ladish_graph_hide_client(virtualizer_ptr->jack_graph, client);
413 else
415 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client);
416 ladish_client_destroy(client);
420 static
421 void
422 port_appeared(
423 void * context,
424 uint64_t client_id,
425 uint64_t port_id,
426 const char * real_jack_port_name,
427 bool is_input,
428 bool is_terminal,
429 bool is_midi)
431 ladish_client_handle jack_client;
432 ladish_client_handle vclient;
433 ladish_port_handle port;
434 uint32_t type;
435 uint32_t flags;
436 const char * jack_client_name;
437 bool is_a2j;
438 uuid_t client_uuid;
439 char * alsa_client_name;
440 char * alsa_port_name;
441 char * a2j_fake_jack_port_name = NULL;
442 uint32_t alsa_client_id;
443 const char * jack_port_name;
444 const char * vport_name;
445 ladish_graph_handle vgraph;
447 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");
449 type = is_midi ? JACKDBUS_PORT_TYPE_MIDI : JACKDBUS_PORT_TYPE_AUDIO;
450 flags = is_input ? JACKDBUS_PORT_FLAG_INPUT : JACKDBUS_PORT_FLAG_OUTPUT;
451 if (is_terminal)
453 flags |= JACKDBUS_PORT_FLAG_TERMINAL;
456 /********************/
457 /* gather info about the appeared port */
459 jack_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
460 if (jack_client == NULL)
462 log_error("Port of unknown JACK client with id %"PRIu64" appeared", client_id);
463 goto exit;
466 /* find the virtual graph that owns the app that owns the client that owns the appeared port */
467 vgraph = ladish_client_get_vgraph(jack_client);
468 if (vgraph == NULL)
470 vgraph = find_link_port_vgraph_by_uuid(virtualizer_ptr, real_jack_port_name, &port);
471 if (vgraph == NULL)
473 log_error("Cannot find vgraph for appeared jmcore port '%s'", real_jack_port_name);
474 goto exit;
477 /* jmcore port appeared */
479 log_info("jmcore port appeared in vgraph %s", ladish_graph_get_description(vgraph));
481 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, real_jack_port_name, type, flags, false))
483 log_error("ladish_graph_add_port() failed.");
484 goto exit;
487 if (vgraph == g_studio.studio_graph)
489 ladish_port_set_jack_id(port, port_id);
491 else
493 ladish_port_set_jack_id_room(port, port_id);
496 vclient = ladish_graph_get_port_client(vgraph, port);
497 if (vclient == NULL)
499 log_error("link port client not found in vgraph %s", ladish_graph_get_description(vgraph));
500 ASSERT_NO_PASS;
501 goto exit;
504 ladish_graph_show_port(vgraph, port);
505 goto exit;
508 ladish_client_get_uuid(jack_client, client_uuid);
509 is_a2j = uuid_compare(client_uuid, g_a2j_uuid) == 0;
510 if (is_a2j)
512 log_info("a2j port appeared");
513 if (!a2j_proxy_map_jack_port(real_jack_port_name, &alsa_client_name, &alsa_port_name, &alsa_client_id))
515 is_a2j = false;
517 else
519 log_info("a2j: '%s':'%s' (%"PRIu32")", alsa_client_name, alsa_port_name, alsa_client_id);
522 a2j_fake_jack_port_name = catdup4(alsa_client_name, is_input ? " (playback)" : " (capture)", ": ", alsa_port_name);
523 if (a2j_fake_jack_port_name == NULL)
525 log_error("catdup4() failed");
526 goto free_alsa_names;
529 jack_port_name = a2j_fake_jack_port_name;
531 else
533 jack_port_name = real_jack_port_name;
536 jack_client_name = ladish_graph_get_client_name(virtualizer_ptr->jack_graph, jack_client);
538 /********************/
540 /* search (by name) the appeared port in jack graph
541 * if found - show it in both graphs.
542 * if not found - create new port and add it to the jack graph.
543 * Then process to adding it to virtual graph */
545 port = ladish_graph_find_port_by_name(virtualizer_ptr->jack_graph, jack_client, jack_port_name);
546 if (port != NULL)
548 log_info("found existing port");
550 if (ladish_port_get_jack_id(port) != 0)
552 log_error("Ignoring duplicate JACK port '%s':'%s'", jack_client_name, jack_port_name);
553 goto free_alsa_names;
556 ladish_port_set_jack_id(port, port_id);
557 ladish_graph_adjust_port(virtualizer_ptr->jack_graph, port, type, flags);
558 ladish_graph_show_port(virtualizer_ptr->jack_graph, port);
560 vclient = ladish_graph_get_port_client(vgraph, port);
561 if (vclient == NULL)
563 log_error("JACK port not found in virtual graph");
564 ASSERT_NO_PASS;
565 goto free_alsa_names;
568 ladish_client_set_jack_id(vclient, client_id);
569 ladish_graph_adjust_port(vgraph, port, type, flags);
570 ladish_graph_show_port(vgraph, port);
571 goto free_alsa_names;
574 if (!ladish_port_create(NULL, false, &port))
576 log_error("ladish_port_create() failed.");
577 goto free_alsa_names;
580 /* set port jack id so invisible connections to/from it can be restored */
581 ladish_port_set_jack_id(port, port_id);
583 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, jack_port_name, type, flags, false))
585 log_error("ladish_graph_add_port() failed.");
586 ladish_port_destroy(port);
587 goto free_alsa_names;
590 /********************/
591 /* find/create the virtual client where port will be added */
593 if (is_a2j)
595 vclient = ladish_graph_find_client_by_name(vgraph, alsa_client_name);
596 if (vclient == NULL)
598 if (!ladish_client_create(NULL, &vclient))
600 log_error("ladish_client_create() failed.");
601 goto free_alsa_names;
604 if (!ladish_graph_add_client(vgraph, vclient, alsa_client_name, false))
606 log_error("ladish_graph_add_client() failed.");
607 ladish_client_destroy(vclient);
608 goto free_alsa_names;
613 else if (client_id == virtualizer_ptr->system_client_id)
615 log_info("system client port appeared");
617 if (!is_input)
618 { /* output capture port */
620 vclient = ladish_graph_find_client_by_uuid(vgraph, g_system_capture_uuid);
621 if (vclient == NULL)
623 if (!ladish_client_create(g_system_capture_uuid, &vclient))
625 log_error("ladish_client_create() failed.");
626 goto free_alsa_names;
629 if (!ladish_graph_add_client(vgraph, vclient, "Hardware Capture", false))
631 log_error("ladish_graph_add_client() failed.");
632 ladish_client_destroy(vclient);
633 goto free_alsa_names;
637 else
638 { /* input playback port */
639 vclient = ladish_graph_find_client_by_uuid(vgraph, g_system_playback_uuid);
640 if (vclient == NULL)
642 if (!ladish_client_create(g_system_playback_uuid, &vclient))
644 log_error("ladish_client_create() failed.");
645 goto free_alsa_names;
648 if (!ladish_graph_add_client(vgraph, vclient, "Hardware Playback", false))
650 ladish_client_destroy(vclient);
651 goto free_alsa_names;
656 else
657 { /* non-system client */
658 log_info("non-system client port appeared");
660 vclient = ladish_graph_find_client_by_jack_id(vgraph, client_id);
661 if (vclient == NULL)
663 if (!ladish_client_create(NULL, &vclient))
665 log_error("ladish_client_create() failed.");
666 goto free_alsa_names;
669 ladish_client_set_jack_id(vclient, client_id);
671 if (!ladish_graph_add_client(vgraph, vclient, jack_client_name, false))
673 log_error("ladish_graph_add_client() failed to add client '%s' to virtual graph", jack_client_name);
674 ladish_client_destroy(vclient);
675 goto free_alsa_names;
680 /********************/
681 /* add newly appeared port to the virtual graph */
683 if (is_a2j)
685 vport_name = alsa_port_name;
687 else
689 vport_name = jack_port_name;
692 if (!ladish_graph_add_port(vgraph, vclient, port, vport_name, type, flags, false))
694 log_error("ladish_graph_add_port() failed.");
695 goto free_alsa_names;
698 free_alsa_names:
699 if (a2j_fake_jack_port_name != NULL)
701 free(a2j_fake_jack_port_name);
704 if (is_a2j)
706 free(alsa_client_name);
707 free(alsa_port_name);
710 exit:
711 return;
714 static void port_disappeared(void * context, uint64_t client_id, uint64_t port_id)
716 ladish_client_handle client;
717 ladish_port_handle port;
718 ladish_graph_handle vgraph;
719 bool jmcore;
721 log_info("port_disappeared(%"PRIu64", %"PRIu64")", client_id, port_id);
723 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
724 if (client == NULL)
726 log_error("Port of unknown JACK client with id %"PRIu64" disappeared", client_id);
727 return;
730 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
731 if (port == NULL)
733 log_error("Unknown JACK port with id %"PRIu64" disappeared", port_id);
734 return;
737 /* find the virtual graph that owns the app that owns the client that owns the disappeared port */
738 jmcore = false;
739 vgraph = ladish_client_get_vgraph(client);
740 if (vgraph == NULL)
742 vgraph = find_link_port_vgraph_by_uuid(virtualizer_ptr, ladish_graph_get_port_name(virtualizer_ptr->jack_graph, port), NULL);
743 if (vgraph == NULL)
745 log_error("Cannot find vgraph for disappeared jmcore port");
747 else
749 jmcore = true;
750 ladish_graph_remove_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
754 if (true) /* if client is supposed to be persisted */
756 if (!jmcore)
758 ladish_port_set_jack_id(port, 0);
759 ladish_graph_hide_port(virtualizer_ptr->jack_graph, port);
761 if (vgraph != NULL)
763 ladish_graph_hide_port(vgraph, port);
764 client = ladish_graph_get_port_client(vgraph, port);
765 if (ladish_graph_is_client_looks_empty(vgraph, client))
767 ladish_graph_hide_client(vgraph, client);
771 else
773 if (!jmcore)
775 ladish_graph_remove_port(virtualizer_ptr->jack_graph, port);
778 if (vgraph != NULL)
780 client = ladish_graph_remove_port(vgraph, port);
781 if (client != NULL)
783 if (ladish_graph_is_client_empty(vgraph, client))
785 ladish_graph_remove_client(vgraph, client);
792 static void port_renamed(void * context, uint64_t client_id, uint64_t port_id, const char * old_port_name, const char * new_port_name)
794 ladish_client_handle client;
795 ladish_port_handle port;
796 ladish_graph_handle vgraph;
798 log_info("port_renamed(%"PRIu64", '%s', '%s')", port_id, old_port_name, new_port_name);
800 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
801 if (client == NULL)
803 log_error("Port of unknown JACK client with id %"PRIu64" was renamed", client_id);
804 return;
807 /* find the virtual graph that owns the app that owns the client that owns the renamed port */
808 vgraph = ladish_client_get_vgraph(client);
810 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id, true, true);
811 if (port == NULL)
813 log_error("Unknown JACK port with id %"PRIu64" was renamed", port_id);
814 return;
817 if (!ladish_graph_rename_port(virtualizer_ptr->jack_graph, port, new_port_name))
819 log_error("renaming of port in jack graph failed");
822 if (!ladish_graph_rename_port(vgraph, port, new_port_name))
824 log_error("renaming of port in virtual graph failed");
828 static bool ports_connect_request(void * context, ladish_graph_handle graph_handle, ladish_port_handle port1, ladish_port_handle port2)
830 uint64_t port1_id;
831 uint64_t port2_id;
833 ASSERT(ladish_graph_get_opath(graph_handle)); /* studio or room virtual graph */
834 log_info("virtualizer: ports connect request");
836 if (graph_handle == g_studio.studio_graph)
838 port1_id = ladish_port_get_jack_id(port1);
839 port2_id = ladish_port_get_jack_id(port2);
841 else
843 port1_id = ladish_port_get_jack_id_room(port1);
844 port2_id = ladish_port_get_jack_id_room(port2);
847 graph_proxy_connect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
849 return true;
852 static bool ports_disconnect_request(void * context, ladish_graph_handle graph_handle, uint64_t connection_id)
854 ladish_port_handle port1;
855 ladish_port_handle port2;
856 uint64_t port1_id;
857 uint64_t port2_id;
859 ASSERT(ladish_graph_get_opath(graph_handle)); /* studio or room virtual graph */
860 log_info("virtualizer: ports disconnect request");
862 if (!ladish_graph_get_connection_ports(graph_handle, connection_id, &port1, &port2))
864 log_error("cannot find ports that are disconnect-requested");
865 ASSERT_NO_PASS;
866 return false;
869 if (graph_handle == g_studio.studio_graph)
871 port1_id = ladish_port_get_jack_id(port1);
872 port2_id = ladish_port_get_jack_id(port2);
874 else
876 port1_id = ladish_port_get_jack_id_room(port1);
877 port2_id = ladish_port_get_jack_id_room(port2);
880 graph_proxy_disconnect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
882 return true;
885 static void ports_connected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
887 ladish_port_handle port1;
888 ladish_port_handle port2;
889 uint64_t connection_id;
890 ladish_graph_handle vgraph1;
891 ladish_graph_handle vgraph2;
893 log_info("ports_connected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
895 if (!lookup_port(virtualizer_ptr, port1_id, &port1, &vgraph1))
897 return;
900 if (!lookup_port(virtualizer_ptr, port2_id, &port2, &vgraph2))
902 return;
905 if (vgraph1 != vgraph2)
907 /* TODO */
908 log_error("ignoring connection with endpoints in different vgraphs");
909 return;
912 ladish_graph_add_connection(virtualizer_ptr->jack_graph, port1, port2, false);
914 if (ladish_graph_find_connection(vgraph1, port1, port2, &connection_id))
916 log_info("showing hidden virtual connection");
917 ladish_graph_show_connection(vgraph1, connection_id);
919 else
921 log_info("creating new virtual connection");
922 ladish_graph_add_connection(vgraph1, port1, port2, false);
926 static void ports_disconnected(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_disconnected %"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 if (ladish_graph_find_connection(virtualizer_ptr->jack_graph, port1, port2, &connection_id))
955 ladish_graph_remove_connection(virtualizer_ptr->jack_graph, connection_id, true);
957 else
959 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the JACK graph", client1_id, port1_id, client2_id, port2_id);
962 if (ladish_graph_find_connection(vgraph1, port1, port2, &connection_id))
964 ladish_graph_remove_connection(vgraph1, connection_id, false);
966 else
968 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the virtual graph", client1_id, port1_id, client2_id, port2_id);
972 #undef virtualizer_ptr
974 bool
975 ladish_virtualizer_create(
976 graph_proxy_handle jack_graph_proxy,
977 ladish_graph_handle jack_graph,
978 ladish_virtualizer_handle * handle_ptr)
980 struct virtualizer * virtualizer_ptr;
982 virtualizer_ptr = malloc(sizeof(struct virtualizer));
983 if (virtualizer_ptr == NULL)
985 log_error("malloc() failed for struct virtualizer");
986 return false;
989 virtualizer_ptr->jack_graph_proxy = jack_graph_proxy;
990 virtualizer_ptr->jack_graph = jack_graph;
991 virtualizer_ptr->system_client_id = 0;
992 virtualizer_ptr->our_clients_count = 0;
994 if (!graph_proxy_attach(
995 jack_graph_proxy,
996 virtualizer_ptr,
997 clear,
998 client_appeared,
999 NULL, /* jackdbus does not have client rename functionality (yet) */
1000 client_disappeared,
1001 port_appeared,
1002 port_renamed,
1003 port_disappeared,
1004 ports_connected,
1005 ports_disconnected))
1007 free(virtualizer_ptr);
1008 return false;
1011 *handle_ptr = (ladish_virtualizer_handle)virtualizer_ptr;
1012 return true;
1015 #define virtualizer_ptr ((struct virtualizer *)handle)
1017 void
1018 ladish_virtualizer_set_graph_connection_handlers(
1019 ladish_virtualizer_handle handle,
1020 ladish_graph_handle graph)
1022 ladish_graph_set_connection_handlers(graph, virtualizer_ptr, ports_connect_request, ports_disconnect_request);
1025 unsigned int
1026 ladish_virtualizer_get_our_clients_count(
1027 ladish_virtualizer_handle handle)
1029 return virtualizer_ptr->our_clients_count;
1032 void
1033 ladish_virtualizer_destroy(
1034 ladish_virtualizer_handle handle)
1036 log_info("ladish_virtualizer_destroy() called");
1038 graph_proxy_detach((graph_proxy_handle)handle, virtualizer_ptr);
1039 free(virtualizer_ptr);
1042 #undef virtualizer_ptr