daemon: fix a2j handling
[ladish.git] / daemon / virtualizer.c
blob707a67d68a852eada2a27556538cc08d2c098edd
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;
43 /* 47c1cd18-7b21-4389-bec4-6e0658e1d6b1 */
44 UUID_DEFINE(g_system_capture_uuid,0x47,0xC1,0xCD,0x18,0x7B,0x21,0x43,0x89,0xBE,0xC4,0x6E,0x06,0x58,0xE1,0xD6,0xB1);
46 /* b2a0bb06-28d8-4bfe-956e-eb24378f9629 */
47 UUID_DEFINE(g_system_playback_uuid,0xB2,0xA0,0xBB,0x06,0x28,0xD8,0x4B,0xFE,0x95,0x6E,0xEB,0x24,0x37,0x8F,0x96,0x29);
49 /* be23a242-e2b2-11de-b795-002618af5e42 */
50 UUID_DEFINE(g_a2j_uuid,0xBE,0x23,0xA2,0x42,0xE2,0xB2,0x11,0xDE,0xB7,0x95,0x00,0x26,0x18,0xAF,0x5E,0x42);
52 char * get_app_name(struct virtualizer * virtualizer_ptr, uint64_t client_id)
54 int64_t pid;
55 unsigned long long ppid;
56 char * app_name;
58 if (!graph_proxy_get_client_pid(virtualizer_ptr->jack_graph_proxy, client_id, &pid))
60 pid = 0;
63 log_info("client pid is %"PRId64, pid);
64 app_name = NULL;
65 if (pid != 0)
67 ppid = (unsigned long long)pid;
68 loop:
69 app_name = ladish_app_supervisor_search_app(g_studio.app_supervisor, ppid);
70 if (app_name == NULL)
72 ppid = procfs_get_process_parent(ppid);
73 if (ppid != 0)
75 //log_info("parent pid %llu", ppid);
76 goto loop;
81 return app_name;
84 #define virtualizer_ptr ((struct virtualizer *)context)
86 static void clear(void * context)
88 log_info("clear");
91 static void client_appeared(void * context, uint64_t id, const char * name)
93 ladish_client_handle client;
94 const char * a2j_name;
95 bool is_a2j;
96 char * app_name = NULL;
98 log_info("client_appeared(%"PRIu64", %s)", id, name);
100 a2j_name = a2j_proxy_get_jack_client_name_cached();
101 is_a2j = a2j_name != NULL && strcmp(a2j_name, name) == 0;
103 if (is_a2j)
105 client = ladish_graph_find_client_by_uuid(virtualizer_ptr->jack_graph, g_a2j_uuid);
107 else
109 app_name = get_app_name(virtualizer_ptr, id);
110 if (app_name != NULL)
112 log_info("app name is '%s'", app_name);
113 name = app_name;
116 client = ladish_graph_find_client_by_name(virtualizer_ptr->jack_graph, name);
119 if (client != NULL)
121 log_info("found existing client");
122 ASSERT(ladish_client_get_jack_id(client) == 0); /* two JACK clients with same name? */
123 ladish_client_set_jack_id(client, id);
124 ladish_graph_show_client(virtualizer_ptr->jack_graph, client);
125 goto exit;
128 if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, true, false, false, &client))
130 log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, name);
131 return;
134 ladish_client_set_jack_id(client, id);
136 if (!ladish_graph_add_client(virtualizer_ptr->jack_graph, client, name, false))
138 log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
139 ladish_client_destroy(client);
140 return;
143 exit:
144 if (strcmp(name, "system") == 0)
146 virtualizer_ptr->system_client_id = id;
149 if (app_name != NULL)
151 free(app_name);
155 static void client_disappeared(void * context, uint64_t id)
157 ladish_client_handle client;
159 log_info("client_disappeared(%"PRIu64")", id);
161 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, id);
162 if (client == NULL)
164 log_error("Unknown JACK client with id %"PRIu64" disappeared", id);
165 return;
168 if (id == virtualizer_ptr->system_client_id)
170 virtualizer_ptr->system_client_id = 0;
173 if (true) /* if client is supposed to be persisted */
175 ladish_client_set_jack_id(client, 0);
176 ladish_graph_hide_client(virtualizer_ptr->jack_graph, client);
178 else
180 ladish_graph_remove_client(virtualizer_ptr->jack_graph, client, false);
181 ladish_client_destroy(client);
185 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)
187 ladish_client_handle jack_client;
188 ladish_client_handle studio_client;
189 ladish_port_handle port;
190 uint32_t type;
191 uint32_t flags;
192 const char * jack_client_name;
193 bool is_a2j;
194 uuid_t client_uuid;
195 char * alsa_client_name;
196 char * alsa_port_name;
197 uint32_t alsa_client_id;
199 log_info("port_appeared(%"PRIu64", %"PRIu64", %s (%s, %s))", client_id, port_id, port_name, is_input ? "in" : "out", is_midi ? "midi" : "audio");
201 jack_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
202 if (jack_client == NULL)
204 log_error("Port of unknown JACK client with id %"PRIu64" appeared", client_id);
205 goto fail;
208 ladish_client_get_uuid(jack_client, client_uuid);
209 is_a2j = uuid_compare(client_uuid, g_a2j_uuid) == 0;
210 if (is_a2j)
212 log_info("a2j port appeared");
213 if (!a2j_proxy_map_jack_port(port_name, &alsa_client_name, &alsa_port_name, &alsa_client_id))
215 is_a2j = false;
217 else
219 log_info("a2j: '%s':'%s' (%"PRIu32")", alsa_client_name, alsa_port_name, alsa_client_id);
223 jack_client_name = ladish_graph_get_client_name(virtualizer_ptr->jack_graph, jack_client);
225 type = is_midi ? JACKDBUS_PORT_TYPE_MIDI : JACKDBUS_PORT_TYPE_AUDIO;
226 flags = is_input ? JACKDBUS_PORT_FLAG_INPUT : JACKDBUS_PORT_FLAG_OUTPUT;
227 if (is_terminal)
229 flags |= JACKDBUS_PORT_FLAG_TERMINAL;
232 port = ladish_graph_find_port_by_name(virtualizer_ptr->jack_graph, jack_client, port_name);
233 if (port != NULL)
235 log_info("found existing port");
237 ASSERT(ladish_port_get_jack_id(port) == 0); /* two JACK ports with same name? */
238 ladish_port_set_jack_id(port, port_id);
239 ladish_graph_adjust_port(virtualizer_ptr->jack_graph, port, type, flags);
240 ladish_graph_show_port(virtualizer_ptr->jack_graph, port);
242 studio_client = ladish_graph_get_port_client(virtualizer_ptr->studio_graph, port);
243 if (studio_client == NULL)
245 log_error("JACK port not found in studio graph");
246 ASSERT_NO_PASS;
247 goto free_alsa_names;
250 ladish_client_set_jack_id(studio_client, client_id);
251 ladish_graph_adjust_port(virtualizer_ptr->studio_graph, port, type, flags);
252 ladish_graph_show_port(virtualizer_ptr->studio_graph, port);
253 goto free_alsa_names;
256 if (!ladish_port_create(NULL, &port))
258 log_error("ladish_port_create() failed.");
259 goto free_alsa_names;
262 ladish_port_set_jack_id(port, port_id);
264 if (!ladish_graph_add_port(virtualizer_ptr->jack_graph, jack_client, port, port_name, type, flags, false))
266 log_error("ladish_graph_add_port() failed.");
267 ladish_port_destroy(port);
268 goto free_alsa_names;
271 if (is_a2j)
273 studio_client = ladish_graph_find_client_by_name(virtualizer_ptr->studio_graph, alsa_client_name);
274 if (studio_client == NULL)
276 if (!ladish_client_create(g_a2j_uuid, true, false, true, &studio_client))
278 log_error("ladish_client_create() failed.");
279 goto free_alsa_names;
282 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, alsa_client_name, false))
284 log_error("ladish_graph_add_client() failed.");
285 ladish_client_destroy(studio_client);
286 goto free_alsa_names;
290 port_name = alsa_port_name;
292 else if (client_id == virtualizer_ptr->system_client_id)
294 log_info("system client port appeared");
296 if (!is_input)
297 { /* output capture port */
299 studio_client = ladish_graph_find_client_by_uuid(virtualizer_ptr->studio_graph, g_system_capture_uuid);
300 if (studio_client == NULL)
302 if (!ladish_client_create(g_system_capture_uuid, true, false, true, &studio_client))
304 log_error("ladish_client_create() failed.");
305 goto free_alsa_names;
308 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, "Hardware Capture", false))
310 log_error("ladish_graph_add_client() failed.");
311 ladish_client_destroy(studio_client);
312 goto free_alsa_names;
316 else
317 { /* input playback port */
318 studio_client = ladish_graph_find_client_by_uuid(virtualizer_ptr->studio_graph, g_system_playback_uuid);
319 if (studio_client == NULL)
321 if (!ladish_client_create(g_system_playback_uuid, true, false, true, &studio_client))
323 log_error("ladish_client_create() failed.");
324 goto free_alsa_names;
327 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, "Hardware Playback", false))
329 ladish_client_destroy(studio_client);
330 goto free_alsa_names;
335 else
336 { /* non-system client */
337 log_info("non-system client port appeared");
339 studio_client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->studio_graph, client_id);
340 if (studio_client == NULL)
342 if (!ladish_client_create(NULL, false, false, false, &studio_client))
344 log_error("ladish_client_create() failed.");
345 goto free_alsa_names;
348 ladish_client_set_jack_id(studio_client, client_id);
350 if (!ladish_graph_add_client(virtualizer_ptr->studio_graph, studio_client, jack_client_name, false))
352 log_error("ladish_graph_add_client() failed to add client '%s' to studio graph", jack_client_name);
353 ladish_client_destroy(studio_client);
354 goto free_alsa_names;
359 if (!ladish_graph_add_port(virtualizer_ptr->studio_graph, studio_client, port, port_name, type, flags, false))
361 log_error("ladish_graph_add_port() failed.");
362 goto free_alsa_names;
365 free_alsa_names:
366 if (is_a2j)
368 free(alsa_client_name);
369 free(alsa_port_name);
372 fail:
373 return;
376 static void port_disappeared(void * context, uint64_t client_id, uint64_t port_id)
378 ladish_client_handle client;
379 ladish_port_handle port;
381 log_info("port_disappeared(%"PRIu64")", port_id);
383 client = ladish_graph_find_client_by_jack_id(virtualizer_ptr->jack_graph, client_id);
384 if (client == NULL)
386 log_error("Port of unknown JACK client with id %"PRIu64" disappeared", client_id);
387 return;
390 port = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port_id);
391 if (port == NULL)
393 log_error("Unknown JACK port with id %"PRIu64" disappeared", port_id);
394 return;
397 if (true) /* if client is supposed to be persisted */
399 ladish_port_set_jack_id(port, 0);
400 ladish_graph_hide_port(virtualizer_ptr->jack_graph, port);
401 ladish_graph_hide_port(virtualizer_ptr->studio_graph, port);
402 client = ladish_graph_get_port_client(virtualizer_ptr->studio_graph, port);
403 if (ladish_graph_is_client_looks_empty(virtualizer_ptr->studio_graph, client))
405 ladish_graph_hide_client(virtualizer_ptr->studio_graph, client);
408 else
410 ladish_graph_remove_port(virtualizer_ptr->jack_graph, port);
412 client = ladish_graph_remove_port(virtualizer_ptr->studio_graph, port);
413 if (client != NULL)
415 if (ladish_graph_is_client_empty(virtualizer_ptr->studio_graph, client))
417 ladish_graph_remove_client(virtualizer_ptr->studio_graph, client, false);
423 static bool ports_connect_request(void * context, ladish_graph_handle graph_handle, ladish_port_handle port1, ladish_port_handle port2)
425 uint64_t port1_id;
426 uint64_t port2_id;
428 ASSERT(graph_handle == virtualizer_ptr->studio_graph);
429 log_info("virtualizer: ports connect request");
431 port1_id = ladish_port_get_jack_id(port1);
432 port2_id = ladish_port_get_jack_id(port2);
434 graph_proxy_connect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
436 return true;
439 static bool ports_disconnect_request(void * context, ladish_graph_handle graph_handle, uint64_t connection_id)
441 ladish_port_handle port1;
442 ladish_port_handle port2;
443 uint64_t port1_id;
444 uint64_t port2_id;
446 log_info("virtualizer: ports disconnect request");
448 ASSERT(graph_handle == virtualizer_ptr->studio_graph);
450 if (!ladish_graph_get_connection_ports(virtualizer_ptr->studio_graph, connection_id, &port1, &port2))
452 log_error("cannot find ports that are disconnect-requested");
453 ASSERT_NO_PASS;
454 return false;
457 port1_id = ladish_port_get_jack_id(port1);
458 port2_id = ladish_port_get_jack_id(port2);
460 graph_proxy_disconnect_ports(virtualizer_ptr->jack_graph_proxy, port1_id, port2_id);
462 return true;
465 static void ports_connected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
467 ladish_port_handle port1;
468 ladish_port_handle port2;
469 uint64_t connection_id;
471 log_info("ports_connected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
473 port1 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port1_id);
474 if (port1 == NULL)
476 log_error("Unknown JACK port with id %"PRIu64" connected", port1_id);
477 return;
480 port2 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port2_id);
481 if (port2 == NULL)
483 log_error("Unknown JACK port with id %"PRIu64" connected", port2_id);
484 return;
487 ladish_graph_add_connection(virtualizer_ptr->jack_graph, port1, port2, false);
489 if (ladish_graph_find_connection(virtualizer_ptr->studio_graph, port1, port2, &connection_id))
491 log_info("showing hidden connection");
492 ladish_graph_show_connection(virtualizer_ptr->studio_graph, connection_id);
494 else
496 log_info("creating new connection");
497 ladish_graph_add_connection(virtualizer_ptr->studio_graph, port1, port2, false);
501 static void ports_disconnected(void * context, uint64_t client1_id, uint64_t port1_id, uint64_t client2_id, uint64_t port2_id)
503 ladish_port_handle port1;
504 ladish_port_handle port2;
505 uint64_t connection_id;
507 log_info("ports_disconnected %"PRIu64":%"PRIu64" %"PRIu64":%"PRIu64"", client1_id, port1_id, client2_id, port2_id);
509 port1 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port1_id);
510 if (port1 == NULL)
512 log_error("Unknown JACK port with id %"PRIu64" disconnected", port1_id);
513 return;
516 port2 = ladish_graph_find_port_by_jack_id(virtualizer_ptr->jack_graph, port2_id);
517 if (port2 == NULL)
519 log_error("Unknown JACK port with id %"PRIu64" disconnected", port2_id);
520 return;
523 if (ladish_graph_find_connection(virtualizer_ptr->jack_graph, port1, port2, &connection_id))
525 ladish_graph_remove_connection(virtualizer_ptr->jack_graph, connection_id);
527 else
529 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the JACK graph", client1_id, port1_id, client2_id, port2_id);
532 if (ladish_graph_find_connection(virtualizer_ptr->studio_graph, port1, port2, &connection_id))
534 ladish_graph_remove_connection(virtualizer_ptr->studio_graph, connection_id);
536 else
538 log_error("ports %"PRIu64":%"PRIu64" and %"PRIu64":%"PRIu64" are not connected in the Studio graph", client1_id, port1_id, client2_id, port2_id);
542 #undef virtualizer_ptr
544 bool
545 ladish_virtualizer_create(
546 graph_proxy_handle jack_graph_proxy,
547 ladish_graph_handle jack_graph,
548 ladish_graph_handle studio_graph,
549 ladish_virtualizer_handle * handle_ptr)
551 struct virtualizer * virtualizer_ptr;
553 virtualizer_ptr = malloc(sizeof(struct virtualizer));
554 if (virtualizer_ptr == NULL)
556 log_error("malloc() failed for struct virtualizer");
557 return false;
560 virtualizer_ptr->jack_graph_proxy = jack_graph_proxy;
561 virtualizer_ptr->jack_graph = jack_graph;
562 virtualizer_ptr->studio_graph = studio_graph;
563 virtualizer_ptr->system_client_id = 0;
565 if (!graph_proxy_attach(
566 jack_graph_proxy,
567 virtualizer_ptr,
568 clear,
569 client_appeared,
570 client_disappeared,
571 port_appeared,
572 port_disappeared,
573 ports_connected,
574 ports_disconnected))
576 free(virtualizer_ptr);
577 return false;
580 ladish_graph_set_connection_handlers(studio_graph, virtualizer_ptr, ports_connect_request, ports_disconnect_request);
582 *handle_ptr = (ladish_virtualizer_handle)virtualizer_ptr;
583 return true;
586 #define virtualizer_ptr ((struct virtualizer *)handle)
588 void
589 ladish_virtualizer_destroy(
590 ladish_virtualizer_handle handle)
592 log_info("ladish_virtualizer_destroy() called");
594 graph_proxy_detach((graph_proxy_handle)handle, virtualizer_ptr);
597 #undef virtualizer_ptr