1 /* PipeWire
2 *
3 * Copyright © 2020 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "manager.h"
26
27 #include <spa/pod/iter.h>
28 #include <spa/pod/parser.h>
29 #include <spa/utils/result.h>
30 #include <spa/utils/string.h>
31 #include <pipewire/extensions/metadata.h>
32
33 #include "log.h"
34 #include "module-protocol-pulse/server.h"
35
36 #define MAX_PARAMS 32
37
38 #define manager_emit_sync(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, sync, 0)
39 #define manager_emit_added(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, added, 0, o)
40 #define manager_emit_updated(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, updated, 0, o)
41 #define manager_emit_removed(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, removed, 0, o)
42 #define manager_emit_metadata(m,o,s,k,t,v) spa_hook_list_call(&m->hooks, struct pw_manager_events, metadata,0,o,s,k,t,v)
43 #define manager_emit_disconnect(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, disconnect, 0)
44
45 struct object;
46
47 struct manager {
48 struct pw_manager this;
49
50 struct spa_hook core_listener;
51 struct spa_hook registry_listener;
52 int sync_seq;
53
54 struct spa_hook_list hooks;
55 };
56
57 struct object_info {
58 const char *type;
59 uint32_t version;
60 const void *events;
61 void (*init) (struct object *object);
62 void (*destroy) (struct object *object);
63 };
64
65 struct object_data {
66 struct spa_list link;
67 const char *id;
68 size_t size;
69 };
70
71 struct object {
72 struct pw_manager_object this;
73
74 struct manager *manager;
75
76 const struct object_info *info;
77
78 struct spa_list pending_list;
79
80 struct spa_hook proxy_listener;
81 struct spa_hook object_listener;
82
83 int param_seq[MAX_PARAMS];
84
85 struct spa_list data_list;
86 };
87
core_sync(struct manager * m)88 static int core_sync(struct manager *m)
89 {
90 m->sync_seq = pw_core_sync(m->this.core, PW_ID_CORE, m->sync_seq);
91 pw_log_debug("sync start %u", m->sync_seq);
92 return m->sync_seq;
93 }
94
clear_params(struct spa_list * param_list,uint32_t id)95 static uint32_t clear_params(struct spa_list *param_list, uint32_t id)
96 {
97 struct pw_manager_param *p, *t;
98 uint32_t count = 0;
99
100 spa_list_for_each_safe(p, t, param_list, link) {
101 if (id == SPA_ID_INVALID || p->id == id) {
102 spa_list_remove(&p->link);
103 free(p);
104 count++;
105 }
106 }
107 return count;
108 }
109
add_param(struct spa_list * params,int seq,int * param_seq,uint32_t id,const struct spa_pod * param)110 static struct pw_manager_param *add_param(struct spa_list *params,
111 int seq, int *param_seq, uint32_t id, const struct spa_pod *param)
112 {
113 struct pw_manager_param *p;
114
115 if (id == SPA_ID_INVALID) {
116 if (param == NULL || !spa_pod_is_object(param)) {
117 errno = EINVAL;
118 return NULL;
119 }
120 id = SPA_POD_OBJECT_ID(param);
121 }
122
123 if (id >= MAX_PARAMS) {
124 pw_log_error("too big param id %d", id);
125 errno = EINVAL;
126 return NULL;
127 }
128
129 if (seq != param_seq[id]) {
130 pw_log_debug("ignoring param %d, seq:%d != current_seq:%d",
131 id, seq, param_seq[id]);
132 errno = EBUSY;
133 return NULL;
134 }
135
136 p = malloc(sizeof(*p) + (param != NULL ? SPA_POD_SIZE(param) : 0));
137 if (p == NULL)
138 return NULL;
139
140 p->id = id;
141 if (param != NULL) {
142 p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod);
143 memcpy(p->param, param, SPA_POD_SIZE(param));
144 } else {
145 clear_params(params, id);
146 p->param = NULL;
147 }
148 spa_list_append(params, &p->link);
149
150 return p;
151 }
152
has_param(struct spa_list * param_list,struct pw_manager_param * p)153 static bool has_param(struct spa_list *param_list, struct pw_manager_param *p)
154 {
155 struct pw_manager_param *t;
156 spa_list_for_each(t, param_list, link) {
157 if (p->id == t->id &&
158 SPA_POD_SIZE(p->param) == SPA_POD_SIZE(t->param) &&
159 memcmp(p->param, t->param, SPA_POD_SIZE(p->param)) == 0)
160 return true;
161 }
162 return false;
163 }
164
165
find_object(struct manager * m,uint32_t id)166 static struct object *find_object(struct manager *m, uint32_t id)
167 {
168 struct object *o;
169 spa_list_for_each(o, &m->this.object_list, this.link) {
170 if (o->this.id == id)
171 return o;
172 }
173 return NULL;
174 }
175
object_update_params(struct object * o)176 static void object_update_params(struct object *o)
177 {
178 struct pw_manager_param *p;
179
180 spa_list_consume(p, &o->pending_list, link) {
181 spa_list_remove(&p->link);
182 if (p->param == NULL) {
183 clear_params(&o->this.param_list, p->id);
184 free(p);
185 } else {
186 spa_list_append(&o->this.param_list, &p->link);
187 }
188 }
189 }
190
object_destroy(struct object * o)191 static void object_destroy(struct object *o)
192 {
193 struct manager *m = o->manager;
194 struct object_data *d;
195 spa_list_remove(&o->this.link);
196 m->this.n_objects--;
197 if (o->this.proxy)
198 pw_proxy_destroy(o->this.proxy);
199 pw_properties_free(o->this.props);
200 if (o->this.message_object_path)
201 free(o->this.message_object_path);
202 clear_params(&o->this.param_list, SPA_ID_INVALID);
203 clear_params(&o->pending_list, SPA_ID_INVALID);
204 spa_list_consume(d, &o->data_list, link) {
205 spa_list_remove(&d->link);
206 free(d);
207 }
208 free(o);
209 }
210
211 /* core */
212 static const struct object_info core_info = {
213 .type = PW_TYPE_INTERFACE_Core,
214 .version = PW_VERSION_CORE,
215 };
216
217 /* client */
client_event_info(void * object,const struct pw_client_info * info)218 static void client_event_info(void *object, const struct pw_client_info *info)
219 {
220 struct object *o = object;
221 int changed = 0;
222
223 pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
224
225 info = o->this.info = pw_client_info_merge(o->this.info, info, o->this.changed == 0);
226
227 if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS)
228 changed++;
229
230 if (changed) {
231 o->this.changed += changed;
232 core_sync(o->manager);
233 }
234 }
235
236 static const struct pw_client_events client_events = {
237 PW_VERSION_CLIENT_EVENTS,
238 .info = client_event_info,
239 };
240
client_destroy(struct object * o)241 static void client_destroy(struct object *o)
242 {
243 if (o->this.info) {
244 pw_client_info_free(o->this.info);
245 o->this.info = NULL;
246 }
247 }
248
249 static const struct object_info client_info = {
250 .type = PW_TYPE_INTERFACE_Client,
251 .version = PW_VERSION_CLIENT,
252 .events = &client_events,
253 .destroy = client_destroy,
254 };
255
256 /* module */
module_event_info(void * object,const struct pw_module_info * info)257 static void module_event_info(void *object, const struct pw_module_info *info)
258 {
259 struct object *o = object;
260 int changed = 0;
261
262 pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
263
264 info = o->this.info = pw_module_info_merge(o->this.info, info, o->this.changed == 0);
265
266 if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS)
267 changed++;
268
269 if (changed) {
270 o->this.changed += changed;
271 core_sync(o->manager);
272 }
273 }
274
275 static const struct pw_module_events module_events = {
276 PW_VERSION_MODULE_EVENTS,
277 .info = module_event_info,
278 };
279
module_destroy(struct object * o)280 static void module_destroy(struct object *o)
281 {
282 if (o->this.info) {
283 pw_module_info_free(o->this.info);
284 o->this.info = NULL;
285 }
286 }
287
288 static const struct object_info module_info = {
289 .type = PW_TYPE_INTERFACE_Module,
290 .version = PW_VERSION_MODULE,
291 .events = &module_events,
292 .destroy = module_destroy,
293 };
294
295 /* device */
device_event_info(void * object,const struct pw_device_info * info)296 static void device_event_info(void *object, const struct pw_device_info *info)
297 {
298 struct object *o = object;
299 uint32_t i, changed = 0;
300
301 pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
302
303 info = o->this.info = pw_device_info_merge(o->this.info, info, o->this.changed == 0);
304
305 if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS)
306 changed++;
307
308 if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) {
309 for (i = 0; i < info->n_params; i++) {
310 uint32_t id = info->params[i].id;
311 int res;
312
313 if (info->params[i].user == 0)
314 continue;
315 info->params[i].user = 0;
316
317 if (id >= MAX_PARAMS) {
318 pw_log_error("too big param id %d", id);
319 continue;
320 }
321
322 switch (id) {
323 case SPA_PARAM_EnumProfile:
324 case SPA_PARAM_Profile:
325 case SPA_PARAM_EnumRoute:
326 changed++;
327 break;
328 case SPA_PARAM_Route:
329 break;
330 }
331 add_param(&o->pending_list, o->param_seq[id], o->param_seq, id, NULL);
332 if (!(info->params[i].flags & SPA_PARAM_INFO_READ))
333 continue;
334
335 res = pw_device_enum_params((struct pw_device*)o->this.proxy,
336 ++o->param_seq[id], id, 0, -1, NULL);
337 if (SPA_RESULT_IS_ASYNC(res))
338 o->param_seq[id] = res;
339 }
340 }
341 if (changed) {
342 o->this.changed += changed;
343 core_sync(o->manager);
344 }
345 }
find_device(struct manager * m,uint32_t card_id,uint32_t device)346 static struct object *find_device(struct manager *m, uint32_t card_id, uint32_t device)
347 {
348 struct object *o;
349
350 spa_list_for_each(o, &m->this.object_list, this.link) {
351 struct pw_node_info *info;
352 const char *str;
353
354 if (!spa_streq(o->this.type, PW_TYPE_INTERFACE_Node))
355 continue;
356
357 if ((info = o->this.info) != NULL &&
358 (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL &&
359 (uint32_t)atoi(str) == card_id &&
360 (str = spa_dict_lookup(info->props, "card.profile.device")) != NULL &&
361 (uint32_t)atoi(str) == device)
362 return o;
363 }
364 return NULL;
365 }
366
device_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)367 static void device_event_param(void *object, int seq,
368 uint32_t id, uint32_t index, uint32_t next,
369 const struct spa_pod *param)
370 {
371 struct object *o = object, *dev;
372 struct manager *m = o->manager;
373 struct pw_manager_param *p;
374
375 p = add_param(&o->pending_list, seq, o->param_seq, id, param);
376 if (p == NULL)
377 return;
378
379 if (id == SPA_PARAM_Route && !has_param(&o->this.param_list, p)) {
380 uint32_t idx, device;
381 if (spa_pod_parse_object(param,
382 SPA_TYPE_OBJECT_ParamRoute, NULL,
383 SPA_PARAM_ROUTE_index, SPA_POD_Int(&idx),
384 SPA_PARAM_ROUTE_device, SPA_POD_Int(&device)) < 0)
385 return;
386
387 if ((dev = find_device(m, o->this.id, device)) != NULL) {
388 dev->this.changed++;
389 core_sync(o->manager);
390 }
391 }
392 }
393
394 static const struct pw_device_events device_events = {
395 PW_VERSION_DEVICE_EVENTS,
396 .info = device_event_info,
397 .param = device_event_param,
398 };
399
device_destroy(struct object * o)400 static void device_destroy(struct object *o)
401 {
402 if (o->this.info) {
403 pw_device_info_free(o->this.info);
404 o->this.info = NULL;
405 }
406 }
407
408 static const struct object_info device_info = {
409 .type = PW_TYPE_INTERFACE_Device,
410 .version = PW_VERSION_DEVICE,
411 .events = &device_events,
412 .destroy = device_destroy,
413 };
414
415 /* node */
node_event_info(void * object,const struct pw_node_info * info)416 static void node_event_info(void *object, const struct pw_node_info *info)
417 {
418 struct object *o = object;
419 uint32_t i, changed = 0;
420
421 pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
422
423 info = o->this.info = pw_node_info_merge(o->this.info, info, o->this.changed == 0);
424
425 if (info->change_mask & PW_NODE_CHANGE_MASK_STATE)
426 changed++;
427
428 if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS)
429 changed++;
430
431 if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) {
432 for (i = 0; i < info->n_params; i++) {
433 uint32_t id = info->params[i].id;
434 int res;
435
436 if (info->params[i].user == 0)
437 continue;
438 info->params[i].user = 0;
439
440 if (id >= MAX_PARAMS) {
441 pw_log_error("too big param id %d", id);
442 continue;
443 }
444
445 changed++;
446 add_param(&o->pending_list, o->param_seq[id], o->param_seq, id, NULL);
447 if (!(info->params[i].flags & SPA_PARAM_INFO_READ))
448 continue;
449
450 res = pw_node_enum_params((struct pw_node*)o->this.proxy,
451 ++o->param_seq[id], id, 0, -1, NULL);
452 if (SPA_RESULT_IS_ASYNC(res))
453 o->param_seq[id] = res;
454 }
455 }
456 if (changed) {
457 o->this.changed += changed;
458 core_sync(o->manager);
459 }
460 }
461
node_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)462 static void node_event_param(void *object, int seq,
463 uint32_t id, uint32_t index, uint32_t next,
464 const struct spa_pod *param)
465 {
466 struct object *o = object;
467 add_param(&o->pending_list, seq, o->param_seq, id, param);
468 }
469
470 static const struct pw_node_events node_events = {
471 PW_VERSION_NODE_EVENTS,
472 .info = node_event_info,
473 .param = node_event_param,
474 };
475
node_destroy(struct object * o)476 static void node_destroy(struct object *o)
477 {
478 if (o->this.info) {
479 pw_node_info_free(o->this.info);
480 o->this.info = NULL;
481 }
482 }
483
484 static const struct object_info node_info = {
485 .type = PW_TYPE_INTERFACE_Node,
486 .version = PW_VERSION_NODE,
487 .events = &node_events,
488 .destroy = node_destroy,
489 };
490
491 /* link */
492 static const struct object_info link_info = {
493 .type = PW_TYPE_INTERFACE_Link,
494 .version = PW_VERSION_LINK,
495 };
496
497 /* metadata */
metadata_property(void * object,uint32_t subject,const char * key,const char * type,const char * value)498 static int metadata_property(void *object,
499 uint32_t subject,
500 const char *key,
501 const char *type,
502 const char *value)
503 {
504 struct object *o = object;
505 struct manager *m = o->manager;
506 manager_emit_metadata(m, &o->this, subject, key, type, value);
507 return 0;
508 }
509
510 static const struct pw_metadata_events metadata_events = {
511 PW_VERSION_METADATA_EVENTS,
512 .property = metadata_property,
513 };
514
metadata_init(struct object * object)515 static void metadata_init(struct object *object)
516 {
517 struct object *o = object;
518 struct manager *m = o->manager;
519 o->this.creating = false;
520 manager_emit_added(m, &o->this);
521 }
522
523 static const struct object_info metadata_info = {
524 .type = PW_TYPE_INTERFACE_Metadata,
525 .version = PW_VERSION_METADATA,
526 .events = &metadata_events,
527 .init = metadata_init,
528 };
529
530 static const struct object_info *objects[] =
531 {
532 &core_info,
533 &module_info,
534 &client_info,
535 &device_info,
536 &node_info,
537 &link_info,
538 &metadata_info,
539 };
540
find_info(const char * type,uint32_t version)541 static const struct object_info *find_info(const char *type, uint32_t version)
542 {
543 size_t i;
544 for (i = 0; i < SPA_N_ELEMENTS(objects); i++) {
545 if (spa_streq(objects[i]->type, type) &&
546 objects[i]->version <= version)
547 return objects[i];
548 }
549 return NULL;
550 }
551
552 static void
destroy_removed(void * data)553 destroy_removed(void *data)
554 {
555 struct object *o = data;
556 pw_proxy_destroy(o->this.proxy);
557 }
558
559 static void
destroy_proxy(void * data)560 destroy_proxy(void *data)
561 {
562 struct object *o = data;
563
564 spa_assert(o->info);
565
566 if (o->info->events)
567 spa_hook_remove(&o->object_listener);
568 spa_hook_remove(&o->proxy_listener);
569
570 if (o->info->destroy)
571 o->info->destroy(o);
572
573 o->this.proxy = NULL;
574 }
575
576 static const struct pw_proxy_events proxy_events = {
577 PW_VERSION_PROXY_EVENTS,
578 .removed = destroy_removed,
579 .destroy = destroy_proxy,
580 };
581
registry_event_global(void * data,uint32_t id,uint32_t permissions,const char * type,uint32_t version,const struct spa_dict * props)582 static void registry_event_global(void *data, uint32_t id,
583 uint32_t permissions, const char *type, uint32_t version,
584 const struct spa_dict *props)
585 {
586 struct manager *m = data;
587 struct object *o;
588 const struct object_info *info;
589 struct pw_proxy *proxy;
590
591 info = find_info(type, version);
592 if (info == NULL)
593 return;
594
595 proxy = pw_registry_bind(m->this.registry,
596 id, type, info->version, 0);
597 if (proxy == NULL)
598 return;
599
600 o = calloc(1, sizeof(*o));
601 if (o == NULL) {
602 pw_log_error("can't alloc object for %u %s/%d: %m", id, type, version);
603 pw_proxy_destroy(proxy);
604 return;
605 }
606 o->this.id = id;
607 o->this.permissions = permissions;
608 o->this.type = info->type;
609 o->this.version = version;
610 o->this.props = props ? pw_properties_new_dict(props) : NULL;
611 o->this.proxy = proxy;
612 o->this.creating = true;
613 spa_list_init(&o->this.param_list);
614 spa_list_init(&o->pending_list);
615 spa_list_init(&o->data_list);
616
617 o->manager = m;
618 o->info = info;
619 spa_list_append(&m->this.object_list, &o->this.link);
620 m->this.n_objects++;
621
622 if (info->events)
623 pw_proxy_add_object_listener(proxy,
624 &o->object_listener,
625 o->info->events, o);
626 pw_proxy_add_listener(proxy,
627 &o->proxy_listener,
628 &proxy_events, o);
629
630 if (info->init)
631 info->init(o);
632
633 core_sync(m);
634 }
635
registry_event_global_remove(void * object,uint32_t id)636 static void registry_event_global_remove(void *object, uint32_t id)
637 {
638 struct manager *m = object;
639 struct object *o;
640
641 if ((o = find_object(m, id)) == NULL)
642 return;
643
644 o->this.removing = true;
645
646 if (!o->this.creating)
647 manager_emit_removed(m, &o->this);
648
649 object_destroy(o);
650 }
651
652 static const struct pw_registry_events registry_events = {
653 PW_VERSION_REGISTRY_EVENTS,
654 .global = registry_event_global,
655 .global_remove = registry_event_global_remove,
656 };
657
on_core_info(void * data,const struct pw_core_info * info)658 static void on_core_info(void *data, const struct pw_core_info *info)
659 {
660 struct manager *m = data;
661 m->this.info = pw_core_info_merge(m->this.info, info, true);
662 }
663
on_core_done(void * data,uint32_t id,int seq)664 static void on_core_done(void *data, uint32_t id, int seq)
665 {
666 struct manager *m = data;
667 struct object *o;
668
669 if (id == PW_ID_CORE) {
670 if (m->sync_seq != seq)
671 return;
672
673 pw_log_debug("sync end %u/%u", m->sync_seq, seq);
674
675 manager_emit_sync(m);
676
677 spa_list_for_each(o, &m->this.object_list, this.link)
678 object_update_params(o);
679
680 spa_list_for_each(o, &m->this.object_list, this.link) {
681 if (o->this.creating) {
682 o->this.creating = false;
683 manager_emit_added(m, &o->this);
684 o->this.changed = 0;
685 } else if (o->this.changed > 0) {
686 manager_emit_updated(m, &o->this);
687 o->this.changed = 0;
688 }
689 }
690 }
691 }
692
on_core_error(void * data,uint32_t id,int seq,int res,const char * message)693 static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
694 {
695 struct manager *m = data;
696
697 if (id == PW_ID_CORE && res == -EPIPE) {
698 pw_log_debug("connection error: %d, %s", res, message);
699 manager_emit_disconnect(m);
700 }
701 }
702
703 static const struct pw_core_events core_events = {
704 PW_VERSION_CORE_EVENTS,
705 .done = on_core_done,
706 .info = on_core_info,
707 .error = on_core_error
708 };
709
pw_manager_new(struct pw_core * core)710 struct pw_manager *pw_manager_new(struct pw_core *core)
711 {
712 struct manager *m;
713
714 m = calloc(1, sizeof(*m));
715 if (m == NULL)
716 return NULL;
717
718 m->this.core = core;
719 m->this.registry = pw_core_get_registry(m->this.core,
720 PW_VERSION_REGISTRY, 0);
721 if (m->this.registry == NULL) {
722 free(m);
723 return NULL;
724 }
725
726 spa_hook_list_init(&m->hooks);
727
728 spa_list_init(&m->this.object_list);
729
730 pw_core_add_listener(m->this.core,
731 &m->core_listener,
732 &core_events, m);
733 pw_registry_add_listener(m->this.registry,
734 &m->registry_listener,
735 ®istry_events, m);
736
737 return &m->this;
738 }
739
pw_manager_add_listener(struct pw_manager * manager,struct spa_hook * listener,const struct pw_manager_events * events,void * data)740 void pw_manager_add_listener(struct pw_manager *manager,
741 struct spa_hook *listener,
742 const struct pw_manager_events *events, void *data)
743 {
744 struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
745 spa_hook_list_append(&m->hooks, listener, events, data);
746 core_sync(m);
747 }
748
pw_manager_set_metadata(struct pw_manager * manager,struct pw_manager_object * metadata,uint32_t subject,const char * key,const char * type,const char * format,...)749 int pw_manager_set_metadata(struct pw_manager *manager,
750 struct pw_manager_object *metadata,
751 uint32_t subject, const char *key, const char *type,
752 const char *format, ...)
753 {
754 struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
755 struct object *s;
756 va_list args;
757 char buf[1024];
758 char *value;
759
760 if ((s = find_object(m, subject)) == NULL)
761 return -ENOENT;
762 if (!SPA_FLAG_IS_SET(s->this.permissions, PW_PERM_M))
763 return -EACCES;
764
765 if (metadata == NULL)
766 return -ENOTSUP;
767 if (!SPA_FLAG_IS_SET(metadata->permissions, PW_PERM_W|PW_PERM_X))
768 return -EACCES;
769 if (metadata->proxy == NULL)
770 return -ENOENT;
771
772 if (type != NULL) {
773 va_start(args, format);
774 vsnprintf(buf, sizeof(buf), format, args);
775 va_end(args);
776 value = buf;
777 } else {
778 spa_assert(format == NULL);
779 value = NULL;
780 }
781
782 pw_metadata_set_property(metadata->proxy,
783 subject, key, type, value);
784 return 0;
785 }
786
pw_manager_for_each_object(struct pw_manager * manager,int (* callback)(void * data,struct pw_manager_object * object),void * data)787 int pw_manager_for_each_object(struct pw_manager *manager,
788 int (*callback) (void *data, struct pw_manager_object *object),
789 void *data)
790 {
791 struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
792 struct object *o;
793 int res;
794
795 spa_list_for_each(o, &m->this.object_list, this.link) {
796 if (o->this.creating)
797 continue;
798 if ((res = callback(data, &o->this)) != 0)
799 return res;
800 }
801 return 0;
802 }
803
pw_manager_destroy(struct pw_manager * manager)804 void pw_manager_destroy(struct pw_manager *manager)
805 {
806 struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
807 struct object *o;
808
809 spa_hook_remove(&m->core_listener);
810
811 spa_list_consume(o, &m->this.object_list, this.link)
812 object_destroy(o);
813
814 spa_hook_remove(&m->registry_listener);
815 pw_proxy_destroy((struct pw_proxy*)m->this.registry);
816
817 if (m->this.info)
818 pw_core_info_free(m->this.info);
819
820 free(m);
821 }
822
object_find_data(struct object * o,const char * id)823 static struct object_data *object_find_data(struct object *o, const char *id)
824 {
825 struct object_data *d;
826 spa_list_for_each(d, &o->data_list, link) {
827 if (spa_streq(d->id, id))
828 return d;
829 }
830 return NULL;
831 }
832
pw_manager_object_add_data(struct pw_manager_object * obj,const char * id,size_t size)833 void *pw_manager_object_add_data(struct pw_manager_object *obj, const char *id, size_t size)
834 {
835 struct object *o = SPA_CONTAINER_OF(obj, struct object, this);
836 struct object_data *d;
837
838 d = object_find_data(o, id);
839 if (d != NULL) {
840 if (d->size == size)
841 goto done;
842 spa_list_remove(&d->link);
843 free(d);
844 }
845
846 d = calloc(1, sizeof(struct object_data) + size);
847 d->id = id;
848 d->size = size;
849
850 spa_list_append(&o->data_list, &d->link);
851
852 done:
853 return SPA_PTROFF(d, sizeof(struct object_data), void);
854 }
855
pw_manager_object_get_data(struct pw_manager_object * obj,const char * id)856 void *pw_manager_object_get_data(struct pw_manager_object *obj, const char *id)
857 {
858 struct object *o = SPA_CONTAINER_OF(obj, struct object, this);
859 struct object_data *d = object_find_data(o, id);
860
861 return d ? SPA_PTROFF(d, sizeof(*d), void) : NULL;
862 }
863
pw_manager_sync(struct pw_manager * manager)864 int pw_manager_sync(struct pw_manager *manager)
865 {
866 struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
867 return core_sync(m);
868 }
869
pw_manager_object_is_client(struct pw_manager_object * o)870 bool pw_manager_object_is_client(struct pw_manager_object *o)
871 {
872 return spa_streq(o->type, PW_TYPE_INTERFACE_Client);
873 }
874
pw_manager_object_is_module(struct pw_manager_object * o)875 bool pw_manager_object_is_module(struct pw_manager_object *o)
876 {
877 return spa_streq(o->type, PW_TYPE_INTERFACE_Module);
878 }
879
pw_manager_object_is_card(struct pw_manager_object * o)880 bool pw_manager_object_is_card(struct pw_manager_object *o)
881 {
882 const char *str;
883 return spa_streq(o->type, PW_TYPE_INTERFACE_Device) &&
884 o->props != NULL &&
885 (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
886 spa_streq(str, "Audio/Device");
887 }
888
pw_manager_object_is_sink(struct pw_manager_object * o)889 bool pw_manager_object_is_sink(struct pw_manager_object *o)
890 {
891 const char *str;
892 return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
893 o->props != NULL &&
894 (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
895 (spa_streq(str, "Audio/Sink") || spa_streq(str, "Audio/Duplex"));
896 }
897
pw_manager_object_is_source(struct pw_manager_object * o)898 bool pw_manager_object_is_source(struct pw_manager_object *o)
899 {
900 const char *str;
901 return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
902 o->props != NULL &&
903 (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
904 (spa_streq(str, "Audio/Source") ||
905 spa_streq(str, "Audio/Duplex") ||
906 spa_streq(str, "Audio/Source/Virtual"));
907 }
908
pw_manager_object_is_monitor(struct pw_manager_object * o)909 bool pw_manager_object_is_monitor(struct pw_manager_object *o)
910 {
911 const char *str;
912 return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
913 o->props != NULL &&
914 (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
915 (spa_streq(str, "Audio/Sink"));
916 }
917
pw_manager_object_is_virtual(struct pw_manager_object * o)918 bool pw_manager_object_is_virtual(struct pw_manager_object *o)
919 {
920 const char *str;
921 struct pw_node_info *info;
922 return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
923 (info = o->info) != NULL && info->props != NULL &&
924 (str = spa_dict_lookup(info->props, PW_KEY_NODE_VIRTUAL)) != NULL &&
925 pw_properties_parse_bool(str);
926 }
927
pw_manager_object_is_source_or_monitor(struct pw_manager_object * o)928 bool pw_manager_object_is_source_or_monitor(struct pw_manager_object *o)
929 {
930 return pw_manager_object_is_source(o) || pw_manager_object_is_monitor(o);
931 }
932
pw_manager_object_is_sink_input(struct pw_manager_object * o)933 bool pw_manager_object_is_sink_input(struct pw_manager_object *o)
934 {
935 const char *str;
936 return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
937 o->props != NULL &&
938 (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
939 spa_streq(str, "Stream/Output/Audio");
940 }
941
pw_manager_object_is_source_output(struct pw_manager_object * o)942 bool pw_manager_object_is_source_output(struct pw_manager_object *o)
943 {
944 const char *str;
945 return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
946 o->props != NULL &&
947 (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
948 spa_streq(str, "Stream/Input/Audio");
949 }
950
pw_manager_object_is_recordable(struct pw_manager_object * o)951 bool pw_manager_object_is_recordable(struct pw_manager_object *o)
952 {
953 return pw_manager_object_is_source(o) || pw_manager_object_is_sink(o) || pw_manager_object_is_sink_input(o);
954 }
955
pw_manager_object_is_link(struct pw_manager_object * o)956 bool pw_manager_object_is_link(struct pw_manager_object *o)
957 {
958 return spa_streq(o->type, PW_TYPE_INTERFACE_Link);
959 }
960