daemon: don't skip a2j when counting ladish started clients
[ladish.git] / daemon / virtualizer.c
blobda05b6a084647c17ed95e0f233d4ed71fec47ea3
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009 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 "a2j_proxy.h"
30 #include "procfs.h"
31 #include "app_supervisor.h"
32 #include "studio_internal.h"
34 struct virtualizer
37 graph_proxy_handle jack_graph_proxy;
38 ladish_graph_handle jack_graph;
39 ladish_graph_handle studio_graph;
40 uint64_t system_client_id;
41 unsigned int our_clients_count;
44 /* 47c1cd18-7b21-4389-bec4-6e0658e1d6b1 */
45 UUID_DEFINE(g_system_capture_uuid,0x47,0xC1,0xCD,0x18,0x7B,0x21,0x43,0x89,0xBE,0xC4,0x6E,0x06,0x58,0xE1,0xD6,0xB1);
47 /* b2a0bb06-28d8-4bfe-956e-eb24378f9629 */
48 UUID_DEFINE(g_system_playback_uuid,0xB2,0xA0,0xBB,0x06,0x28,0xD8,0x4B,0xFE,0x95,0x6E,0xEB,0x24,0x37,0x8F,0x96,0x29);
50 /* be23a242-e2b2-11de-b795-002618af5e42 */
51 UUID_DEFINE(g_a2j_uuid,0xBE,0x23,0xA2,0x42,0xE2,0xB2,0x11,0xDE,0xB7,0x95,0x00,0x26,0x18,0xAF,0x5E,0x42);
53 char * get_app_name(struct virtualizer * virtualizer_ptr, uint64_t client_id, pid_t * app_pid_ptr)
55 int64_t pid;
56 unsigned long long ppid;
57 char * app_name;
59 if (!graph_proxy_get_client_pid(virtualizer_ptr->jack_graph_proxy, client_id, &pid))
61 pid = 0;
64 log_info("client pid is %"PRId64, pid);
65 app_name = NULL;
66 if (pid != 0)
68 ppid = (unsigned long long)pid;
69 loop:
70 app_name = ladish_app_supervisor_search_app(g_studio.app_supervisor, ppid);
71 if (app_name == NULL)
73 ppid = procfs_get_process_parent(ppid);
74 if (ppid != 0)
76 //log_info("parent pid %llu", ppid);
77 goto loop;
82 *app_pid_ptr = (pid_t)ppid;
84 return app_name;
87 #define virtualizer_ptr ((struct virtualizer *)context)
89 static void clear(void * context)
91 log_info("clear");
94 static void client_appeared(void * context, uint64_t id, const char * name)
96 ladish_client_handle client;
97 const char * a2j_name;
98 bool is_a2j;
99 char * app_name;
100 pid_t pid;
102 log_info("client_appeared(%"PRIu64", %s)", id, name);
104 a2j_name = a2j_proxy_get_jack_client_name_cached();
105 is_a2j = a2j_name != NULL && strcmp(a2j_name, name) == 0;
107 app_name = get_app_name(virtualizer_ptr, id, &pid);
108 if (app_name != NULL)
110 log_info("app name is '%s'", app_name);
111 name = app_name;
114 if (is_a2j)
116 client = ladish_graph_find_client_by_uuid(virtualizer_ptr->jack_graph, g_a2j_uuid);
118 else
120 client = ladish_graph_find_client_by_name(virtualizer_ptr->jack_graph, name);
123 if (client != NULL)
125 log_info("found existing client");
126 ASSERT(ladish_client_get_jack_id(client) == 0); /* two JACK clients with same name? */
127 ladish_client_set_jack_id(client, id);
128 ladish_graph_show_client(virtualizer_ptr->jack_graph, client);
129 goto exit;
132 if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, true, false, false, &client))
134 log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, name);
135 return;
138 ladish_client_set_jack_id(client, id);
140 if (!ladish_graph_add_client(virtualizer_ptr->jack_graph, client, name, false))
142 log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
143 ladish_client_destroy(client);
144 return;
147 exit:
148 if (!is_a2j && strcmp(name, "system") == 0)
150 virtualizer_ptr->system_client_id = id;
153 if (app_name != NULL)
155 ladish_client_set_pid(client, pid);
156 virtualizer_ptr->our_clients_count++;
157 free(app_name);
161 static void client_disappeared(void * context, uint64_t id)
163 ladish_client_handle client;
164 pid_t pid;
166 log_info("client_disappeared(%"PRIu64")", id);
168 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, id);
169 if (client == NULL)
171 log_error("Unknown JACK client with id %"PRIu64" disappeared", id);
172 return;
175 pid = ladish_client_get_pid(client);
176 if (pid != 0)
178 virtualizer_ptr->our_clients_count--;
181 if (id == virtualizer_ptr->system_client_id)
183 virtualizer_ptr->system_client_id = 0;
186 if (true) /* if client is supposed to be persisted */
188 ladish_client_set_jack_id(client, 0);
189 ladish_graph_hide_client(virtualizer_ptr->jack_graph, client);
191 else
193 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client, false);
194 ladish_client_destroy(client);
198 static void port_appeared(void * context, uint64_t client_id, uint64_t port_id, const char * port_name, bool is_input, bool is_terminal, bool is_midi)
200 ladish_client_handle jack_client;
201 ladish_client_handle studio_client;
202 ladish_port_handle port;
203 uint32_t type;
204 uint32_t flags;
205 const char * jack_client_name;
206 bool is_a2j;
207 uuid_t client_uuid;
208 char * alsa_client_name;
209 char * alsa_port_name;
210 uint32_t alsa_client_id;
212 log_info("port_appeared(%"PRIu64", %"PRIu64", %s (%s, %s))", client_id, port_id, port_name, is_input ? "in" : "out", is_midi ? "midi" : "audio");
214 /********************/
215 /* gather info about the appeared port */
217 jack_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
218 if (jack_client == NULL)
220 log_error("Port of unknown JACK client with id %"PRIu64" appeared", client_id);
221 goto fail;
224 ladish_client_get_uuid(jack_client, client_uuid);
225 is_a2j = uuid_compare(client_uuid, g_a2j_uuid) == 0;
226 if (is_a2j)
228 log_info("a2j port appeared");
229 if (!a2j_proxy_map_jack_port(port_name, &alsa_client_name, &alsa_port_name, &alsa_client_id))
231 is_a2j = false;
233 else
235 log_info("a2j: '%s':'%s' (%"PRIu32")", alsa_client_name, alsa_port_name, alsa_client_id);
239 jack_client_name = ladish_graph_get_client_name(virtualizer_ptr->jack_graph, jack_client);
241 type = is_midi ? JACKDBUS_PORT_TYPE_MIDI : JACKDBUS_PORT_TYPE_AUDIO;
242 flags = is_input ? JACKDBUS_PORT_FLAG_INPUT : JACKDBUS_PORT_FLAG_OUTPUT;
243 if (is_terminal)
245 flags |= JACKDBUS_PORT_FLAG_TERMINAL;
248 /********************/
250 if (!is_a2j) /* a2j ports dont exist in JACK graph */
252 /* search the appeared port in jack graph
253 * if found - show it in both graphs.
254 * if not found - create new port and add it to the jack graph.
255 * The process to adding it to studio graph */
257 port = ladish_graph_find_port_by_name(virtualizer_ptr->jack_graph, jack_client, port_name);
258 if (port != NULL)
260 log_info("found existing port");
262 ASSERT(ladish_port_get_jack_id(port) == 0); /* two JACK ports with same name? */
263 ladish_port_set_jack_id(port, port_id);
264 ladish_graph_adjust_port(virtualizer_ptr->jack_graph, port, type, flags);
265 ladish_graph_show_port(virtualizer_ptr->jack_graph, port);
267 studio_client = ladish_graph_get_port_client(virtualizer_ptr->studio_graph, port);
268 if (studio_client == NULL)
270 log_error("JACK port not found in studio graph");
271 ASSERT_NO_PASS;
272 goto free_alsa_names;
275 ladish_client_set_jack_id(studio_client, client_id);
276 ladish_graph_adjust_port(virtualizer_ptr->studio_graph, port, type, flags);
277 ladish_graph_show_port(virtualizer_ptr->studio_graph, port);
278 goto free_alsa_names;
281 if (!ladish_port_create(NULL, &port))
283 log_error("ladish_port_create() failed.");
284 goto free_alsa_names;
287 /* set port jack id so invisible connections to/from it can be restored */
288 ladish_port_set_jack_id(port, port_id);
290 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, port_name, type, flags, false))
292 log_error("ladish_graph_add_port() failed.");
293 ladish_port_destroy(port);
294 goto free_alsa_names;
297 else
299 /* a2j ports are present and referenced from the jack graph so the port
300 * will be found/created later, when studio graph is inspected */
301 port = NULL;
304 /********************/
305 /* find/create the studio client where port will be added eventually */
307 if (is_a2j)
309 /* search (by name) the appeared a2j port and its client in the studio graph.
310 * if client is not found - create both client and port.
311 * if client is found - show it if this is the first port visible port.
312 * if port is found - show it
313 * if port is not found - create new port and add it to the studio graph */
314 studio_client = ladish_graph_find_client_by_name(virtualizer_ptr->studio_graph, alsa_client_name);
315 if (studio_client == NULL)
317 if (!ladish_client_create(g_a2j_uuid, true, false, true, &studio_client))
319 log_error("ladish_client_create() failed.");
320 goto free_alsa_names;
323 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, alsa_client_name, false))
325 log_error("ladish_graph_add_client() failed.");
326 ladish_client_destroy(studio_client);
327 goto free_alsa_names;
330 else
332 port = ladish_graph_find_port_by_name(virtualizer_ptr->studio_graph, studio_client, alsa_port_name);
333 if (port != NULL)
334 { /* found existing port - show it */
335 ASSERT(ladish_port_get_jack_id(port) == 0); /* two a2j ports with same name? */
336 ladish_port_set_jack_id(port, port_id); /* set port jack id so invisible connections to/from it can be restored */
337 ladish_graph_adjust_port(virtualizer_ptr->studio_graph, port, type, flags);
338 ladish_graph_show_port(virtualizer_ptr->studio_graph, port);
339 goto free_alsa_names;
343 /* port not found - create it */
344 if (!ladish_port_create(NULL, &port))
346 log_error("ladish_port_create() failed.");
347 goto free_alsa_names;
350 /* set port jack id so invisible connections to/from it can be restored */
351 ladish_port_set_jack_id(port, port_id);
353 port_name = alsa_port_name;
355 else if (client_id == virtualizer_ptr->system_client_id)
357 log_info("system client port appeared");
359 if (!is_input)
360 { /* output capture port */
362 studio_client = ladish_graph_find_client_by_uuid(virtualizer_ptr->studio_graph, g_system_capture_uuid);
363 if (studio_client == NULL)
365 if (!ladish_client_create(g_system_capture_uuid, true, false, true, &studio_client))
367 log_error("ladish_client_create() failed.");
368 goto free_alsa_names;
371 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, "Hardware Capture", false))
373 log_error("ladish_graph_add_client() failed.");
374 ladish_client_destroy(studio_client);
375 goto free_alsa_names;
379 else
380 { /* input playback port */
381 studio_client = ladish_graph_find_client_by_uuid(virtualizer_ptr->studio_graph, g_system_playback_uuid);
382 if (studio_client == NULL)
384 if (!ladish_client_create(g_system_playback_uuid, true, false, true, &studio_client))
386 log_error("ladish_client_create() failed.");
387 goto free_alsa_names;
390 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, "Hardware Playback", false))
392 ladish_client_destroy(studio_client);
393 goto free_alsa_names;
398 else
399 { /* non-system client */
400 log_info("non-system client port appeared");
402 studio_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->studio_graph, client_id);
403 if (studio_client == NULL)
405 if (!ladish_client_create(NULL, false, false, false, &studio_client))
407 log_error("ladish_client_create() failed.");
408 goto free_alsa_names;
411 ladish_client_set_jack_id(studio_client, client_id);
413 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, jack_client_name, false))
415 log_error("ladish_graph_add_client() failed to add client '%s' to studio graph", jack_client_name);
416 ladish_client_destroy(studio_client);
417 goto free_alsa_names;
422 /* add newly appeared port to the studio graph */
424 if (!ladish_graph_add_port(virtualizer_ptr->studio_graph, studio_client, port, port_name, type, flags, false))
426 log_error("ladish_graph_add_port() failed.");
427 goto free_alsa_names;
430 free_alsa_names:
431 if (is_a2j)
433 free(alsa_client_name);
434 free(alsa_port_name);
437 fail:
438 return;
441 static void port_disappeared(void * context, uint64_t client_id, uint64_t port_id)
443 ladish_client_handle client;
444 ladish_port_handle port;
446 log_info("port_disappeared(%"PRIu64")", port_id);
448 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
449 if (client == NULL)
451 log_error("Port of unknown JACK client with id %"PRIu64" disappeared", client_id);
452 return;
455 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id);
456 if (port == NULL)
458 log_error("Unknown JACK port with id %"PRIu64" disappeared", port_id);
459 return;
462 if (true) /* if client is supposed to be persisted */
464 ladish_port_set_jack_id(port, 0);
465 ladish_graph_hide_port(virtualizer_ptr->jack_graph, port);
466 ladish_graph_hide_port(virtualizer_ptr->studio_graph, port);
467 client = ladish_graph_get_port_client(virtualizer_ptr->studio_graph, port);
468 if (ladish_graph_is_client_looks_empty(virtualizer_ptr->studio_graph, client))
470 ladish_graph_hide_client(virtualizer_ptr->studio_graph, client);
473 else
475 ladish_graph_remove_port(virtualizer_ptr->jack_graph, port);
477 client = ladish_graph_remove_port(virtualizer_ptr->studio_graph, port);
478 if (client != NULL)
480 if (ladish_graph_is_client_empty(virtualizer_ptr->studio_graph, client))
482 ladish_graph_remove_client(virtualizer_ptr->studio_graph, client, false);
488 static bool ports_connect_request(void * context, ladish_graph_handle graph_handle, ladish_port_handle port1, ladish_port_handle port2)
490 uint64_t port1_id;
491 uint64_t port2_id;
493 ASSERT(graph_handle == virtualizer_ptr->studio_graph);
494 log_info("virtualizer: ports connect request");
496 port1_id = ladish_port_get_jack_id(port1);
497 port2_id = ladish_port_get_jack_id(port2);
499 graph_proxy_connect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
501 return true;
504 static bool ports_disconnect_request(void * context, ladish_graph_handle graph_handle, uint64_t connection_id)
506 ladish_port_handle port1;
507 ladish_port_handle port2;
508 uint64_t port1_id;
509 uint64_t port2_id;
511 log_info("virtualizer: ports disconnect request");
513 ASSERT(graph_handle == virtualizer_ptr->studio_graph);
515 if (!ladish_graph_get_connection_ports(virtualizer_ptr->studio_graph, connection_id, &port1, &port2))
517 log_error("cannot find ports that are disconnect-requested");
518 ASSERT_NO_PASS;
519 return false;
522 port1_id = ladish_port_get_jack_id(port1);
523 port2_id = ladish_port_get_jack_id(port2);
525 graph_proxy_disconnect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
527 return true;
530 static void ports_connected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
532 ladish_port_handle port1;
533 ladish_port_handle port2;
534 uint64_t connection_id;
536 log_info("ports_connected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
538 port1 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port1_id);
539 if (port1 == NULL)
541 log_error("Unknown JACK port with id %"PRIu64" connected", port1_id);
542 return;
545 port2 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port2_id);
546 if (port2 == NULL)
548 log_error("Unknown JACK port with id %"PRIu64" connected", port2_id);
549 return;
552 ladish_graph_add_connection(virtualizer_ptr->jack_graph, port1, port2, false);
554 if (ladish_graph_find_connection(virtualizer_ptr->studio_graph, port1, port2, &connection_id))
556 log_info("showing hidden connection");
557 ladish_graph_show_connection(virtualizer_ptr->studio_graph, connection_id);
559 else
561 log_info("creating new connection");
562 ladish_graph_add_connection(virtualizer_ptr->studio_graph, port1, port2, false);
566 static void ports_disconnected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
568 ladish_port_handle port1;
569 ladish_port_handle port2;
570 uint64_t connection_id;
572 log_info("ports_disconnected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
574 port1 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port1_id);
575 if (port1 == NULL)
577 log_error("Unknown JACK port with id %"PRIu64" disconnected", port1_id);
578 return;
581 port2 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port2_id);
582 if (port2 == NULL)
584 log_error("Unknown JACK port with id %"PRIu64" disconnected", port2_id);
585 return;
588 if (ladish_graph_find_connection(virtualizer_ptr->jack_graph, port1, port2, &connection_id))
590 ladish_graph_remove_connection(virtualizer_ptr->jack_graph, connection_id);
592 else
594 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the JACK graph", client1_id, port1_id, client2_id, port2_id);
597 if (ladish_graph_find_connection(virtualizer_ptr->studio_graph, port1, port2, &connection_id))
599 ladish_graph_remove_connection(virtualizer_ptr->studio_graph, connection_id);
601 else
603 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the Studio graph", client1_id, port1_id, client2_id, port2_id);
607 #undef virtualizer_ptr
609 bool
610 ladish_virtualizer_create(
611 graph_proxy_handle jack_graph_proxy,
612 ladish_graph_handle jack_graph,
613 ladish_graph_handle studio_graph,
614 ladish_virtualizer_handle * handle_ptr)
616 struct virtualizer * virtualizer_ptr;
618 virtualizer_ptr = malloc(sizeof(struct virtualizer));
619 if (virtualizer_ptr == NULL)
621 log_error("malloc() failed for struct virtualizer");
622 return false;
625 virtualizer_ptr->jack_graph_proxy = jack_graph_proxy;
626 virtualizer_ptr->jack_graph = jack_graph;
627 virtualizer_ptr->studio_graph = studio_graph;
628 virtualizer_ptr->system_client_id = 0;
629 virtualizer_ptr->our_clients_count = 0;
631 if (!graph_proxy_attach(
632 jack_graph_proxy,
633 virtualizer_ptr,
634 clear,
635 client_appeared,
636 client_disappeared,
637 port_appeared,
638 port_disappeared,
639 ports_connected,
640 ports_disconnected))
642 free(virtualizer_ptr);
643 return false;
646 ladish_graph_set_connection_handlers(studio_graph, virtualizer_ptr, ports_connect_request, ports_disconnect_request);
648 *handle_ptr = (ladish_virtualizer_handle)virtualizer_ptr;
649 return true;
652 #define virtualizer_ptr ((struct virtualizer *)handle)
654 unsigned int
655 ladish_virtualizer_get_our_clients_count(
656 ladish_virtualizer_handle handle)
658 return virtualizer_ptr->our_clients_count;
661 void
662 ladish_virtualizer_destroy(
663 ladish_virtualizer_handle handle)
665 log_info("ladish_virtualizer_destroy() called");
667 graph_proxy_detach((graph_proxy_handle)handle, virtualizer_ptr);
668 free(virtualizer_ptr);
671 #undef virtualizer_ptr