rename graph clients when app is renamed
[ladish.git] / daemon / virtualizer.c
blob2791a1c3f025207755d8a0db366e2174c2bcd765
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 "../proxies/a2j_proxy.h"
30 #include "procfs.h"
31 #include "app_supervisor.h"
32 #include "studio_internal.h"
33 #include "../catdup.h"
35 struct virtualizer
38 graph_proxy_handle jack_graph_proxy;
39 ladish_graph_handle jack_graph;
40 ladish_graph_handle studio_graph;
41 uint64_t system_client_id;
42 unsigned int our_clients_count;
45 /* 47c1cd18-7b21-4389-bec4-6e0658e1d6b1 */
46 UUID_DEFINE(g_system_capture_uuid,0x47,0xC1,0xCD,0x18,0x7B,0x21,0x43,0x89,0xBE,0xC4,0x6E,0x06,0x58,0xE1,0xD6,0xB1);
48 /* b2a0bb06-28d8-4bfe-956e-eb24378f9629 */
49 UUID_DEFINE(g_system_playback_uuid,0xB2,0xA0,0xBB,0x06,0x28,0xD8,0x4B,0xFE,0x95,0x6E,0xEB,0x24,0x37,0x8F,0x96,0x29);
51 /* be23a242-e2b2-11de-b795-002618af5e42 */
52 UUID_DEFINE(g_a2j_uuid,0xBE,0x23,0xA2,0x42,0xE2,0xB2,0x11,0xDE,0xB7,0x95,0x00,0x26,0x18,0xAF,0x5E,0x42);
54 char * get_app_name(struct virtualizer * virtualizer_ptr, uint64_t client_id, pid_t * app_pid_ptr)
56 int64_t pid;
57 unsigned long long ppid;
58 char * app_name;
60 if (!graph_proxy_get_client_pid(virtualizer_ptr->jack_graph_proxy, client_id, &pid))
62 pid = 0;
65 log_info("client pid is %"PRId64, pid);
66 app_name = NULL;
67 if (pid != 0)
69 ppid = (unsigned long long)pid;
70 loop:
71 app_name = ladish_app_supervisor_search_app(g_studio.app_supervisor, ppid);
72 if (app_name == NULL)
74 ppid = procfs_get_process_parent(ppid);
75 if (ppid != 0)
77 //log_info("parent pid %llu", ppid);
78 goto loop;
82 else
84 ppid = 0;
87 *app_pid_ptr = (pid_t)ppid;
89 return app_name;
92 #define virtualizer_ptr ((struct virtualizer *)context)
94 static void clear(void * context)
96 log_info("clear");
99 static void client_appeared(void * context, uint64_t id, const char * jack_name)
101 ladish_client_handle client;
102 const char * a2j_name;
103 bool is_a2j;
104 char * app_name;
105 const char * name;
106 pid_t pid;
108 log_info("client_appeared(%"PRIu64", %s)", id, jack_name);
110 a2j_name = a2j_proxy_get_jack_client_name_cached();
111 is_a2j = a2j_name != NULL && strcmp(a2j_name, jack_name) == 0;
113 app_name = get_app_name(virtualizer_ptr, id, &pid);
114 if (app_name != NULL)
116 log_info("app name is '%s'", app_name);
117 name = app_name;
119 else
121 name = jack_name;
124 if (is_a2j)
126 client = ladish_graph_find_client_by_uuid(virtualizer_ptr->jack_graph, g_a2j_uuid);
128 else
130 client = ladish_graph_find_client_by_name(virtualizer_ptr->jack_graph, name);
133 if (client != NULL)
135 log_info("found existing client");
136 if (ladish_client_get_jack_id(client) != 0)
138 log_error("Ignoring client with duplicate name '%s' ('%s')", name, jack_name);
139 goto free_app_name;
142 ladish_client_set_jack_id(client, id);
143 ladish_graph_show_client(virtualizer_ptr->jack_graph, client);
144 goto done;
147 if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, true, false, false, &client))
149 log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, jack_name);
150 goto free_app_name;
153 ladish_client_set_jack_id(client, id);
155 if (!ladish_graph_add_client(virtualizer_ptr->jack_graph, client, name, false))
157 log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
158 ladish_client_destroy(client);
159 goto free_app_name;
162 done:
163 if (strcmp(jack_name, "system") == 0)
165 virtualizer_ptr->system_client_id = id;
168 if (app_name != NULL)
170 ladish_client_set_pid(client, pid);
171 virtualizer_ptr->our_clients_count++;
174 free_app_name:
175 if (app_name != NULL)
177 free(app_name);
181 static void client_disappeared(void * context, uint64_t id)
183 ladish_client_handle client;
184 pid_t pid;
186 log_info("client_disappeared(%"PRIu64")", id);
188 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, id);
189 if (client == NULL)
191 log_error("Unknown JACK client with id %"PRIu64" disappeared", id);
192 return;
195 log_info("client disappeared: '%s'", ladish_graph_get_client_name(virtualizer_ptr->jack_graph, client));
197 pid = ladish_client_get_pid(client);
198 if (pid != 0)
200 virtualizer_ptr->our_clients_count--;
203 if (id == virtualizer_ptr->system_client_id)
205 virtualizer_ptr->system_client_id = 0;
208 if (true) /* if client is supposed to be persisted */
210 ladish_client_set_jack_id(client, 0);
211 ladish_graph_hide_client(virtualizer_ptr->jack_graph, client);
213 else
215 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client, false);
216 ladish_client_destroy(client);
220 static
221 void
222 port_appeared(
223 void * context,
224 uint64_t client_id,
225 uint64_t port_id,
226 const char * real_jack_port_name,
227 bool is_input,
228 bool is_terminal,
229 bool is_midi)
231 ladish_client_handle jack_client;
232 ladish_client_handle studio_client;
233 ladish_port_handle port;
234 uint32_t type;
235 uint32_t flags;
236 const char * jack_client_name;
237 bool is_a2j;
238 uuid_t client_uuid;
239 char * alsa_client_name;
240 char * alsa_port_name;
241 char * a2j_fake_jack_port_name = NULL;
242 uint32_t alsa_client_id;
243 const char * jack_port_name;
244 const char * studio_port_name;
246 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");
248 /********************/
249 /* gather info about the appeared port */
251 jack_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
252 if (jack_client == NULL)
254 log_error("Port of unknown JACK client with id %"PRIu64" appeared", client_id);
255 goto fail;
258 ladish_client_get_uuid(jack_client, client_uuid);
259 is_a2j = uuid_compare(client_uuid, g_a2j_uuid) == 0;
260 if (is_a2j)
262 log_info("a2j port appeared");
263 if (!a2j_proxy_map_jack_port(real_jack_port_name, &alsa_client_name, &alsa_port_name, &alsa_client_id))
265 is_a2j = false;
267 else
269 log_info("a2j: '%s':'%s' (%"PRIu32")", alsa_client_name, alsa_port_name, alsa_client_id);
272 a2j_fake_jack_port_name = catdup4(alsa_client_name, is_input ? " (playback)" : " (capture)", ": ", alsa_port_name);
273 if (a2j_fake_jack_port_name == NULL)
275 log_error("catdup4() failed");
276 goto free_alsa_names;
279 jack_port_name = a2j_fake_jack_port_name;
281 else
283 jack_port_name = real_jack_port_name;
286 jack_client_name = ladish_graph_get_client_name(virtualizer_ptr->jack_graph, jack_client);
288 type = is_midi ? JACKDBUS_PORT_TYPE_MIDI : JACKDBUS_PORT_TYPE_AUDIO;
289 flags = is_input ? JACKDBUS_PORT_FLAG_INPUT : JACKDBUS_PORT_FLAG_OUTPUT;
290 if (is_terminal)
292 flags |= JACKDBUS_PORT_FLAG_TERMINAL;
295 /********************/
297 /* search (by name) the appeared port in jack graph
298 * if found - show it in both graphs.
299 * if not found - create new port and add it to the jack graph.
300 * The process to adding it to studio graph */
302 port = ladish_graph_find_port_by_name(virtualizer_ptr->jack_graph, jack_client, jack_port_name);
303 if (port != NULL)
305 log_info("found existing port");
307 if (ladish_port_get_jack_id(port) != 0)
309 log_error("Ignoring duplicate JACK port '%s':'%s'", jack_client_name, jack_port_name);
310 goto free_alsa_names;
313 ladish_port_set_jack_id(port, port_id);
314 ladish_graph_adjust_port(virtualizer_ptr->jack_graph, port, type, flags);
315 ladish_graph_show_port(virtualizer_ptr->jack_graph, port);
317 studio_client = ladish_graph_get_port_client(virtualizer_ptr->studio_graph, port);
318 if (studio_client == NULL)
320 log_error("JACK port not found in studio graph");
321 ASSERT_NO_PASS;
322 goto free_alsa_names;
325 ladish_client_set_jack_id(studio_client, client_id);
326 ladish_graph_adjust_port(virtualizer_ptr->studio_graph, port, type, flags);
327 ladish_graph_show_port(virtualizer_ptr->studio_graph, port);
328 goto free_alsa_names;
331 if (!ladish_port_create(NULL, &port))
333 log_error("ladish_port_create() failed.");
334 goto free_alsa_names;
337 /* set port jack id so invisible connections to/from it can be restored */
338 ladish_port_set_jack_id(port, port_id);
340 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, jack_port_name, type, flags, false))
342 log_error("ladish_graph_add_port() failed.");
343 ladish_port_destroy(port);
344 goto free_alsa_names;
347 /********************/
348 /* find/create the studio client where port will be added */
350 if (is_a2j)
352 studio_client = ladish_graph_find_client_by_name(virtualizer_ptr->studio_graph, alsa_client_name);
353 if (studio_client == NULL)
355 if (!ladish_client_create(NULL, true, false, true, &studio_client))
357 log_error("ladish_client_create() failed.");
358 goto free_alsa_names;
361 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, alsa_client_name, false))
363 log_error("ladish_graph_add_client() failed.");
364 ladish_client_destroy(studio_client);
365 goto free_alsa_names;
370 else if (client_id == virtualizer_ptr->system_client_id)
372 log_info("system client port appeared");
374 if (!is_input)
375 { /* output capture port */
377 studio_client = ladish_graph_find_client_by_uuid(virtualizer_ptr->studio_graph, g_system_capture_uuid);
378 if (studio_client == NULL)
380 if (!ladish_client_create(g_system_capture_uuid, true, false, true, &studio_client))
382 log_error("ladish_client_create() failed.");
383 goto free_alsa_names;
386 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, "Hardware Capture", false))
388 log_error("ladish_graph_add_client() failed.");
389 ladish_client_destroy(studio_client);
390 goto free_alsa_names;
394 else
395 { /* input playback port */
396 studio_client = ladish_graph_find_client_by_uuid(virtualizer_ptr->studio_graph, g_system_playback_uuid);
397 if (studio_client == NULL)
399 if (!ladish_client_create(g_system_playback_uuid, true, false, true, &studio_client))
401 log_error("ladish_client_create() failed.");
402 goto free_alsa_names;
405 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, "Hardware Playback", false))
407 ladish_client_destroy(studio_client);
408 goto free_alsa_names;
413 else
414 { /* non-system client */
415 log_info("non-system client port appeared");
417 studio_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->studio_graph, client_id);
418 if (studio_client == NULL)
420 if (!ladish_client_create(NULL, false, false, false, &studio_client))
422 log_error("ladish_client_create() failed.");
423 goto free_alsa_names;
426 ladish_client_set_jack_id(studio_client, client_id);
428 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, jack_client_name, false))
430 log_error("ladish_graph_add_client() failed to add client '%s' to studio graph", jack_client_name);
431 ladish_client_destroy(studio_client);
432 goto free_alsa_names;
437 /********************/
438 /* add newly appeared port to the studio graph */
440 if (is_a2j)
442 studio_port_name = alsa_port_name;
444 else
446 studio_port_name = jack_port_name;
449 if (!ladish_graph_add_port(virtualizer_ptr->studio_graph, studio_client, port, studio_port_name, type, flags, false))
451 log_error("ladish_graph_add_port() failed.");
452 goto free_alsa_names;
455 free_alsa_names:
456 if (a2j_fake_jack_port_name != NULL)
458 free(a2j_fake_jack_port_name);
461 if (is_a2j)
463 free(alsa_client_name);
464 free(alsa_port_name);
467 fail:
468 return;
471 static void port_disappeared(void * context, uint64_t client_id, uint64_t port_id)
473 ladish_client_handle client;
474 ladish_port_handle port;
476 log_info("port_disappeared(%"PRIu64")", port_id);
478 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
479 if (client == NULL)
481 log_error("Port of unknown JACK client with id %"PRIu64" disappeared", client_id);
482 return;
485 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id);
486 if (port == NULL)
488 log_error("Unknown JACK port with id %"PRIu64" disappeared", port_id);
489 return;
492 if (true) /* if client is supposed to be persisted */
494 ladish_port_set_jack_id(port, 0);
495 ladish_graph_hide_port(virtualizer_ptr->jack_graph, port);
496 ladish_graph_hide_port(virtualizer_ptr->studio_graph, port);
497 client = ladish_graph_get_port_client(virtualizer_ptr->studio_graph, port);
498 if (ladish_graph_is_client_looks_empty(virtualizer_ptr->studio_graph, client))
500 ladish_graph_hide_client(virtualizer_ptr->studio_graph, client);
503 else
505 ladish_graph_remove_port(virtualizer_ptr->jack_graph, port);
507 client = ladish_graph_remove_port(virtualizer_ptr->studio_graph, port);
508 if (client != NULL)
510 if (ladish_graph_is_client_empty(virtualizer_ptr->studio_graph, client))
512 ladish_graph_remove_client(virtualizer_ptr->studio_graph, client, false);
518 static void port_renamed(void * context, uint64_t client_id, uint64_t port_id, const char * old_port_name, const char * new_port_name)
520 ladish_client_handle client;
521 ladish_port_handle port;
523 log_info("port_renamed(%"PRIu64", '%s', '%s')", port_id, old_port_name, new_port_name);
525 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
526 if (client == NULL)
528 log_error("Port of unknown JACK client with id %"PRIu64" was renamed", client_id);
529 return;
532 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id);
533 if (port == NULL)
535 log_error("Unknown JACK port with id %"PRIu64" was renamed", port_id);
536 return;
539 if (!ladish_graph_rename_port(virtualizer_ptr->jack_graph, port, new_port_name))
541 log_error("renaming of port in jack graph failed");
544 if (!ladish_graph_rename_port(virtualizer_ptr->studio_graph, port, new_port_name))
546 log_error("renaming of port in jack studio failed");
550 static bool ports_connect_request(void * context, ladish_graph_handle graph_handle, ladish_port_handle port1, ladish_port_handle port2)
552 uint64_t port1_id;
553 uint64_t port2_id;
555 ASSERT(graph_handle == virtualizer_ptr->studio_graph);
556 log_info("virtualizer: ports connect request");
558 port1_id = ladish_port_get_jack_id(port1);
559 port2_id = ladish_port_get_jack_id(port2);
561 graph_proxy_connect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
563 return true;
566 static bool ports_disconnect_request(void * context, ladish_graph_handle graph_handle, uint64_t connection_id)
568 ladish_port_handle port1;
569 ladish_port_handle port2;
570 uint64_t port1_id;
571 uint64_t port2_id;
573 log_info("virtualizer: ports disconnect request");
575 ASSERT(graph_handle == virtualizer_ptr->studio_graph);
577 if (!ladish_graph_get_connection_ports(virtualizer_ptr->studio_graph, connection_id, &port1, &port2))
579 log_error("cannot find ports that are disconnect-requested");
580 ASSERT_NO_PASS;
581 return false;
584 port1_id = ladish_port_get_jack_id(port1);
585 port2_id = ladish_port_get_jack_id(port2);
587 graph_proxy_disconnect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
589 return true;
592 static void ports_connected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
594 ladish_port_handle port1;
595 ladish_port_handle port2;
596 uint64_t connection_id;
598 log_info("ports_connected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
600 port1 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port1_id);
601 if (port1 == NULL)
603 log_error("Unknown JACK port with id %"PRIu64" connected", port1_id);
604 return;
607 port2 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port2_id);
608 if (port2 == NULL)
610 log_error("Unknown JACK port with id %"PRIu64" connected", port2_id);
611 return;
614 ladish_graph_add_connection(virtualizer_ptr->jack_graph, port1, port2, false);
616 if (ladish_graph_find_connection(virtualizer_ptr->studio_graph, port1, port2, &connection_id))
618 log_info("showing hidden studio connection");
619 ladish_graph_show_connection(virtualizer_ptr->studio_graph, connection_id);
621 else
623 log_info("creating new studio connection");
624 ladish_graph_add_connection(virtualizer_ptr->studio_graph, port1, port2, false);
628 static void ports_disconnected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
630 ladish_port_handle port1;
631 ladish_port_handle port2;
632 uint64_t connection_id;
634 log_info("ports_disconnected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
636 port1 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port1_id);
637 if (port1 == NULL)
639 log_error("Unknown JACK port with id %"PRIu64" disconnected", port1_id);
640 return;
643 port2 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port2_id);
644 if (port2 == NULL)
646 log_error("Unknown JACK port with id %"PRIu64" disconnected", port2_id);
647 return;
650 if (ladish_graph_find_connection(virtualizer_ptr->jack_graph, port1, port2, &connection_id))
652 ladish_graph_remove_connection(virtualizer_ptr->jack_graph, connection_id, true);
654 else
656 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the JACK graph", client1_id, port1_id, client2_id, port2_id);
659 if (ladish_graph_find_connection(virtualizer_ptr->studio_graph, port1, port2, &connection_id))
661 ladish_graph_remove_connection(virtualizer_ptr->studio_graph, connection_id, false);
663 else
665 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the Studio graph", client1_id, port1_id, client2_id, port2_id);
669 #undef virtualizer_ptr
671 bool
672 ladish_virtualizer_create(
673 graph_proxy_handle jack_graph_proxy,
674 ladish_graph_handle jack_graph,
675 ladish_graph_handle studio_graph,
676 ladish_virtualizer_handle * handle_ptr)
678 struct virtualizer * virtualizer_ptr;
680 virtualizer_ptr = malloc(sizeof(struct virtualizer));
681 if (virtualizer_ptr == NULL)
683 log_error("malloc() failed for struct virtualizer");
684 return false;
687 virtualizer_ptr->jack_graph_proxy = jack_graph_proxy;
688 virtualizer_ptr->jack_graph = jack_graph;
689 virtualizer_ptr->studio_graph = studio_graph;
690 virtualizer_ptr->system_client_id = 0;
691 virtualizer_ptr->our_clients_count = 0;
693 if (!graph_proxy_attach(
694 jack_graph_proxy,
695 virtualizer_ptr,
696 clear,
697 client_appeared,
698 NULL, /* jackdbus does not have client rename functionality (yet) */
699 client_disappeared,
700 port_appeared,
701 port_renamed,
702 port_disappeared,
703 ports_connected,
704 ports_disconnected))
706 free(virtualizer_ptr);
707 return false;
710 ladish_graph_set_connection_handlers(studio_graph, virtualizer_ptr, ports_connect_request, ports_disconnect_request);
712 *handle_ptr = (ladish_virtualizer_handle)virtualizer_ptr;
713 return true;
716 #define virtualizer_ptr ((struct virtualizer *)handle)
718 unsigned int
719 ladish_virtualizer_get_our_clients_count(
720 ladish_virtualizer_handle handle)
722 return virtualizer_ptr->our_clients_count;
725 void
726 ladish_virtualizer_destroy(
727 ladish_virtualizer_handle handle)
729 log_info("ladish_virtualizer_destroy() called");
731 graph_proxy_detach((graph_proxy_handle)handle, virtualizer_ptr);
732 free(virtualizer_ptr);
735 #undef virtualizer_ptr