From 1cbad898620c01b97b9e76b79285cbbb61c8ff48 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sun, 25 Oct 2020 15:28:42 +0100 Subject: [PATCH] pulse-server: add introspection of clients and modules Add manager object to collect object info Wait for object info until completing client connect Implement clients and modules list and info. --- src/modules/meson.build | 3 +- src/modules/module-protocol-pulse/manager.c | 368 ++++++++++++++++++ src/modules/module-protocol-pulse/manager.h | 91 +++++ src/modules/module-protocol-pulse/message.c | 10 +- .../module-protocol-pulse/pulse-server.c | 179 +++++++-- 5 files changed, 615 insertions(+), 36 deletions(-) create mode 100644 src/modules/module-protocol-pulse/manager.c create mode 100644 src/modules/module-protocol-pulse/manager.h diff --git a/src/modules/meson.build b/src/modules/meson.build index 997b87fc5..09df62cd6 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -90,7 +90,8 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ pipewire_module_protocol_pulse = shared_library('pipewire-module-protocol-pulse', [ 'module-protocol-pulse.c', - 'module-protocol-pulse/pulse-server.c' ], + 'module-protocol-pulse/pulse-server.c', + 'module-protocol-pulse/manager.c' ], c_args : pipewire_module_c_args, include_directories : [configinc, spa_inc], install : true, diff --git a/src/modules/module-protocol-pulse/manager.c b/src/modules/module-protocol-pulse/manager.c new file mode 100644 index 000000000..428027473 --- /dev/null +++ b/src/modules/module-protocol-pulse/manager.c @@ -0,0 +1,368 @@ +/* PipeWire + * + * Copyright © 2020 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "manager.h" + +#define manager_emit_sync(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, sync, 0) +#define manager_emit_added(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, added, 0, o) +#define manager_emit_updated(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, updated, 0, o) +#define manager_emit_removed(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, removed, 0, o) + +struct manager { + struct pw_core *core; + struct spa_hook core_listener; + int sync_seq; + + struct pw_registry *registry; + struct spa_hook registry_listener; + + struct spa_hook_list hooks; + + uint32_t n_objects; + struct spa_list object_list; +}; + +struct object_info { + const char *type; + uint32_t version; + const void *events; + void (*destroy) (void *object); +}; + +struct object { + struct pw_manager_object this; + + struct manager *manager; + struct spa_list link; + + const struct object_info *info; + + struct spa_hook proxy_listener; + struct spa_hook object_listener; + + unsigned int new:1; +}; + +static void core_sync(struct manager *m) +{ + m->sync_seq = pw_core_sync(m->core, PW_ID_CORE, m->sync_seq); +} + +static struct object *find_object(struct manager *m, uint32_t id) +{ + struct object *o; + spa_list_for_each(o, &m->object_list, link) { + if (o->this.id == id) + return o; + } + return NULL; +} + +static void object_destroy(struct object *o) +{ + struct manager *m = o->manager; + if (o->this.proxy) + pw_proxy_destroy(o->this.proxy); + free(o->this.type); + if (o->this.props) + pw_properties_free(o->this.props); + spa_list_remove(&o->link); + m->n_objects--; + free(o); +} + +/* client */ +static void client_event_info(void *object, const struct pw_client_info *info) +{ + struct object *o = object; + pw_log_debug("object %p: id:%d change-mask:%"PRIu64, o, o->this.id, info->change_mask); + info = o->this.info = pw_client_info_update(o->this.info, info); +} + +static const struct pw_client_events client_events = { + PW_VERSION_CLIENT_EVENTS, + .info = client_event_info, +}; + +static void client_destroy(void *data) +{ + struct object *o = data; + if (o->this.info) + pw_client_info_free(o->this.info); +} + +struct object_info client_info = { + .type = PW_TYPE_INTERFACE_Client, + .version = PW_VERSION_CLIENT, + .events = &client_events, + .destroy = client_destroy, +}; + +/* module */ +static void module_event_info(void *object, const struct pw_module_info *info) +{ + struct object *o = object; + pw_log_debug("object %p: id:%d change-mask:%"PRIu64, o, o->this.id, info->change_mask); + info = o->this.info = pw_module_info_update(o->this.info, info); +} + +static const struct pw_module_events module_events = { + PW_VERSION_MODULE_EVENTS, + .info = module_event_info, +}; + +static void module_destroy(void *data) +{ + struct object *o = data; + if (o->this.info) + pw_module_info_free(o->this.info); +} + +struct object_info module_info = { + .type = PW_TYPE_INTERFACE_Module, + .version = PW_VERSION_MODULE, + .events = &module_events, + .destroy = module_destroy, +}; + + +static const struct object_info *objects[] = +{ +// &core_info, + &module_info, + &client_info, +}; + +static const struct object_info *find_info(const char *type, uint32_t version) +{ + size_t i; + for (i = 0; i < SPA_N_ELEMENTS(objects); i++) { + if (strcmp(objects[i]->type, type) == 0 && + objects[i]->version <= version) + return objects[i]; + } + return NULL; +} + +static uint32_t clear_params(struct spa_list *param_list, uint32_t id) +{ + struct pw_manager_param *p, *t; + uint32_t count = 0; + + spa_list_for_each_safe(p, t, param_list, link) { + if (id == SPA_ID_INVALID || p->id == id) { + spa_list_remove(&p->link); + free(p); + count++; + } + } + return count; +} + +static void +destroy_removed(void *data) +{ + struct object *o = data; + pw_proxy_destroy(o->this.proxy); +} + +static void +destroy_proxy(void *data) +{ + struct object *o = data; + + clear_params(&o->this.param_list, SPA_ID_INVALID); + + if (o->info && o->info->destroy) + o->info->destroy(o); + + o->this.proxy = NULL; +} + +static const struct pw_proxy_events proxy_events = { + PW_VERSION_PROXY_EVENTS, + .removed = destroy_removed, + .destroy = destroy_proxy, +}; + +static void registry_event_global(void *data, uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const struct spa_dict *props) +{ + struct manager *m = data; + struct object *o; + const struct object_info *info; + struct pw_proxy *proxy; + + info = find_info(type, version); + if (info == NULL) + return; + + proxy = pw_registry_bind(m->registry, + id, type, info->version, 0); + if (proxy == NULL) + return; + + o = calloc(1, sizeof(*o)); + if (o == NULL) { + pw_log_error("can't alloc object for %u %s/%d: %m", id, type, version); + pw_proxy_destroy(proxy); + return; + } + o->this.id = id; + o->this.permissions = permissions; + o->this.type = strdup(type); + o->this.version = version; + o->this.props = props ? pw_properties_new_dict(props) : NULL; + o->this.proxy = proxy; + spa_list_init(&o->this.param_list); + + o->manager = m; + o->info = info; + o->new = true; + spa_list_append(&m->object_list, &o->link); + m->n_objects++; + + pw_proxy_add_object_listener(proxy, + &o->object_listener, + o->info->events, o); + pw_proxy_add_listener(proxy, + &o->proxy_listener, + &proxy_events, o); + + core_sync(m); +} + +static void registry_event_global_remove(void *object, uint32_t id) +{ + struct manager *m = object; + struct object *o; + + if ((o = find_object(m, id)) == NULL) + return; + + manager_emit_removed(m, &o->this); + + object_destroy(o); +} + +static const struct pw_registry_events registry_events = { + PW_VERSION_REGISTRY_EVENTS, + .global = registry_event_global, + .global_remove = registry_event_global_remove, +}; + +static void on_core_done(void *data, uint32_t id, int seq) +{ + struct manager *m = data; + if (id == PW_ID_CORE) { + if (m->sync_seq == seq) { + manager_emit_sync(m); + } + } +} + +static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = on_core_done, +}; + +struct pw_manager *pw_manager_new(struct pw_core *core) +{ + struct manager *this; + + this = calloc(1, sizeof(*this)); + if (this == NULL) + return NULL; + + this->core = core; + spa_hook_list_init(&this->hooks); + + spa_list_init(&this->object_list); + + pw_core_add_listener(this->core, + &this->core_listener, + &core_events, this); + this->registry = pw_core_get_registry(this->core, + PW_VERSION_REGISTRY, 0); + pw_registry_add_listener(this->registry, + &this->registry_listener, + ®istry_events, this); + + return (struct pw_manager*)this; +} + +void pw_manager_add_listener(struct pw_manager *manager, + struct spa_hook *listener, + const struct pw_manager_events *events, void *data) +{ + struct manager *this = (struct manager*)manager; + spa_hook_list_append(&this->hooks, listener, events, data); +} + +struct pw_manager_object *pw_manager_find_object(struct pw_manager *manager, + const char *type, uint32_t id) +{ + struct manager *this = (struct manager*)manager; + struct object *o; + + o = find_object(this, id); + if (o == NULL) + return NULL; + if (type != NULL && strcmp(type, o->this.type) != 0) + return NULL; + return (struct pw_manager_object*)o; +} + +int pw_manager_for_each_object(struct pw_manager *manager, + int (*callback) (void *data, struct pw_manager_object *object), + void *data) +{ + struct manager *this = (struct manager*)manager; + struct object *o; + int res; + + spa_list_for_each(o, &this->object_list, link) { + if ((res = callback(data, &o->this)) != 0) + return res; + } + return 0; +} + +void pw_manager_destroy(struct pw_manager *manager) +{ + struct manager *this = (struct manager*)manager; + struct object *o; + + spa_hook_remove(&this->core_listener); + + spa_list_consume(o, &this->object_list, link) + object_destroy(o); + + if (this->registry) { + spa_hook_remove(&this->registry_listener); + pw_proxy_destroy((struct pw_proxy*)this->registry); + } + free(this); +} diff --git a/src/modules/module-protocol-pulse/manager.h b/src/modules/module-protocol-pulse/manager.h new file mode 100644 index 000000000..31208e338 --- /dev/null +++ b/src/modules/module-protocol-pulse/manager.h @@ -0,0 +1,91 @@ +/* PipeWire + * + * Copyright © 2020 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIPEWIRE_MANAGER_H +#define PIPEWIRE_MANAGER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +struct pw_manager; +struct pw_manager_object; + +struct pw_manager_events { +#define PW_VERSION_MANAGER_EVENTS 0 + uint32_t version; + + void (*destroy) (void *data); + + void (*sync) (void *data); + + void (*added) (void *data, struct pw_manager_object *object); + + void (*updated) (void *data, struct pw_manager_object *object); + + void (*removed) (void *data, struct pw_manager_object *object); +}; + +struct pw_manager_param { + uint32_t id; + struct spa_list link; /**< link in param_list */ + struct spa_pod *param; +}; + +struct pw_manager_object { + uint32_t id; + uint32_t permissions; + char *type; + uint32_t version; + struct pw_properties *props; + + void *info; + struct pw_proxy *proxy; + struct spa_list param_list; +}; + +struct pw_manager *pw_manager_new(struct pw_core *core); + +void pw_manager_add_listener(struct pw_manager *manager, + struct spa_hook *listener, + const struct pw_manager_events *events, void *data); + +void pw_manager_destroy(struct pw_manager *manager); + +struct pw_manager_object *pw_manager_find_object(struct pw_manager *manager, + const char *type, uint32_t id); +int pw_manager_for_each_object(struct pw_manager *manager, + int (*callback) (void *data, struct pw_manager_object *object), + void *data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PIPEWIRE_MANAGER_H */ diff --git a/src/modules/module-protocol-pulse/message.c b/src/modules/module-protocol-pulse/message.c index 6577c4ae1..e3372d1d1 100644 --- a/src/modules/module-protocol-pulse/message.c +++ b/src/modules/module-protocol-pulse/message.c @@ -495,12 +495,12 @@ static void write_cvolume(struct message *m, struct volume *vol) write_32(m, volume_from_linear(vol->values[i])); } -static void write_props(struct message *m, struct pw_properties *props) +static void write_dict(struct message *m, struct spa_dict *dict) { const struct spa_dict_item *it; write_8(m, TAG_PROPLIST); - if (props != NULL) { - spa_dict_for_each(it, &props->dict) { + if (dict != NULL) { + spa_dict_for_each(it, dict) { int l = strlen(it->value); write_string(m, it->key); write_u32(m, l+1); @@ -514,7 +514,7 @@ static void write_format_info(struct message *m, struct format_info *info) { write_8(m, TAG_FORMAT_INFO); write_u8(m, (uint8_t) info->encoding); - write_props(m, info->props); + write_dict(m, info->props ? &info->props->dict : NULL); } static int message_put(struct message *m, ...) @@ -569,7 +569,7 @@ static int message_put(struct message *m, ...) write_cvolume(m, va_arg(va, struct volume*)); break; case TAG_PROPLIST: - write_props(m, va_arg(va, struct pw_properties*)); + write_dict(m, va_arg(va, struct spa_dict*)); break; case TAG_VOLUME: write_volume(m, va_arg(va, double)); diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index cae82ca16..b50660314 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -58,12 +58,20 @@ #include "format.c" #include "message.c" +#include "manager.h" #define NAME "pulse-server" struct impl; struct server; +struct operation { + struct spa_list link; + struct client *client; + uint32_t tag; + void (*callback) (struct operation *op); +}; + struct client { struct spa_list link; struct impl *impl; @@ -71,11 +79,15 @@ struct client { struct spa_source *source; + uint32_t id; uint32_t version; struct pw_properties *props; struct pw_core *core; + struct pw_manager *manager; + struct spa_hook manager_listener; + uint32_t connect_tag; uint32_t in_index; uint32_t out_index; @@ -86,6 +98,8 @@ struct client { struct spa_list free_messages; struct spa_list out_messages; + struct spa_list operations; + unsigned int disconnecting:1; }; @@ -449,10 +463,37 @@ static int do_command_auth(struct client *client, uint32_t command, uint32_t tag return send_message(client, reply); } +static int reply_set_client_name(struct client *client, uint32_t tag) +{ + struct message *reply; + reply = reply_new(client, tag); + + if (client->version >= 13) { + message_put(reply, + TAG_U32, client->id, /* client index */ + TAG_INVALID); + } + return send_message(client, reply); +} + +static void manager_sync(void *data) +{ + struct client *client = data; + + if (client->connect_tag) { + reply_set_client_name(client, client->connect_tag); + client->connect_tag = 0; + } +} + +static const struct pw_manager_events manager_events = { + PW_VERSION_MANAGER_EVENTS, + .sync = manager_sync, +}; + static int do_set_client_name(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; - struct message *reply; const char *name = NULL; int res, changed = 0; @@ -466,12 +507,15 @@ static int do_set_client_name(struct client *client, uint32_t command, uint32_t PW_KEY_APP_NAME, name); } else { if ((res = message_get(m, - TAG_PROPLIST, client->props, + TAG_PROPLIST, client->props ? &client->props->dict : NULL, TAG_INVALID)) < 0) return res; changed++; } + pw_log_info(NAME" %p: SET_CLIENT_NAME %s", impl, + pw_properties_get(client->props, PW_KEY_APP_NAME)); + if (client->core == NULL) { client->core = pw_context_connect(impl->context, pw_properties_copy(client->props), 0); @@ -479,21 +523,22 @@ static int do_set_client_name(struct client *client, uint32_t command, uint32_t res = -errno; goto error; } - } else if (changed){ - pw_core_update_properties(client->core, &client->props->dict); + client->manager = pw_manager_new(client->core); + if (client->manager == NULL) { + res = -errno; + goto error; + } + client->connect_tag = tag; + pw_manager_add_listener(client->manager, &client->manager_listener, + &manager_events, client); + res = 0; + } else { + if (changed) + pw_core_update_properties(client->core, &client->props->dict); + + res = reply_set_client_name(client, tag); } - - pw_log_info(NAME" %p: SET_CLIENT_NAME %s", impl, - pw_properties_get(client->props, PW_KEY_APP_NAME)); - - reply = reply_new(client, tag); - - if (client->version >= 13) { - message_put(reply, - TAG_U32, 0, /* client index */ - TAG_INVALID); - } - return send_message(client, reply); + return res; error: pw_log_error(NAME" %p: failed to connect client: %m", impl); return res; @@ -2145,17 +2190,44 @@ static int do_drain_stream(struct client *client, uint32_t command, uint32_t tag return 0; } -static void fill_client_info(struct client *client, struct message *m) +static void fill_client_info(struct client *client, struct message *m, + struct pw_manager_object *o) { + struct pw_client_info *info = o->info; + message_put(m, - TAG_U32, 0, /* client index */ - TAG_STRING, pw_properties_get(client->props, PW_KEY_APP_NAME), + TAG_U32, o->id, /* client index */ + TAG_STRING, pw_properties_get(o->props, PW_KEY_APP_NAME), TAG_U32, SPA_ID_INVALID, /* module */ TAG_STRING, "PipeWire", /* driver */ TAG_INVALID); if (client->version >= 13) { message_put(m, - TAG_PROPLIST, client->props, + TAG_PROPLIST, info ? info->props : NULL, + TAG_INVALID); + } +} + +static void fill_module_info(struct client *client, struct message *m, + struct pw_manager_object *o) +{ + struct pw_module_info *info = o->info; + + message_put(m, + TAG_U32, o->id, /* module index */ + TAG_STRING, info ? info->name : NULL, + TAG_STRING, info ? info->args : NULL, + TAG_U32, -1, /* n_used */ + TAG_INVALID); + + if (client->version < 15) { + message_put(m, + TAG_BOOLEAN, false, /* auto unload deprecated */ + TAG_INVALID); + } + if (client->version >= 15) { + message_put(m, + TAG_PROPLIST, info ? info->props : NULL, TAG_INVALID); } } @@ -2182,7 +2254,7 @@ static void fill_sink_info(struct client *client, struct message *m, struct devi if (client->version >= 13) { message_put(m, - TAG_PROPLIST, sink->props, + TAG_PROPLIST, sink->props ? &sink->props->dict : NULL, TAG_USEC, 0LL, /* requested latency */ TAG_INVALID); } @@ -2235,7 +2307,7 @@ static void fill_source_info(struct client *client, struct message *m, struct de if (client->version >= 13) { message_put(m, - TAG_PROPLIST, source->props, + TAG_PROPLIST, source->props ? &source->props->dict : NULL, TAG_USEC, 0LL, /* requested latency */ TAG_INVALID); } @@ -2274,6 +2346,7 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st const char *name = NULL; struct device *dev; int res; + struct pw_manager_object *o; if ((res = message_get(m, TAG_U32, &idx, @@ -2301,9 +2374,17 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st reply = reply_new(client, tag); switch (command) { case COMMAND_GET_CLIENT_INFO: - fill_client_info(client, reply); + o = pw_manager_find_object(client->manager, PW_TYPE_INTERFACE_Client, idx); + if (o == NULL) + return reply_error(client, -1, ERR_NOENTITY); + fill_client_info(client, reply, o); break; case COMMAND_GET_MODULE_INFO: + o = pw_manager_find_object(client->manager, PW_TYPE_INTERFACE_Module, idx); + if (o == NULL) + return reply_error(client, -1, ERR_NOENTITY); + fill_module_info(client, reply, o); + break; case COMMAND_GET_CARD_INFO: case COMMAND_GET_SAMPLE_INFO: return reply_error(client, -1, ERR_NOENTITY); @@ -2340,28 +2421,60 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st return send_message(client, reply); } +struct info_list_data { + struct client *client; + struct message *reply; +}; + +static int do_list_clients(void *data, struct pw_manager_object *object) +{ + struct info_list_data *info = data; + + if (strcmp(object->type, PW_TYPE_INTERFACE_Client) != 0) + return 0; + + fill_client_info(info->client, info->reply, object); + return 0; +} + +static int do_list_modules(void *data, struct pw_manager_object *object) +{ + struct info_list_data *info = data; + + if (strcmp(object->type, PW_TYPE_INTERFACE_Module) != 0) + return 0; + + fill_module_info(info->client, info->reply, object); + return 0; +} + static int do_get_info_list(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; - struct message *reply; + struct info_list_data info; + int (*list_func) (void *data, struct pw_manager_object *object) = NULL; pw_log_info(NAME" %p: %s", impl, commands[command].name); - reply = reply_new(client, tag); + info.client = client; + info.reply = reply_new(client, tag); + switch (command) { case COMMAND_GET_CLIENT_INFO_LIST: - fill_client_info(client, reply); + list_func = do_list_clients; break; case COMMAND_GET_MODULE_INFO_LIST: + list_func = do_list_modules; + break; case COMMAND_GET_CARD_INFO_LIST: case COMMAND_GET_SAMPLE_INFO_LIST: break; case COMMAND_GET_SINK_INFO_LIST: - fill_sink_info(client, reply, &impl->default_sink); + fill_sink_info(client, info.reply, &impl->default_sink); break; case COMMAND_GET_SOURCE_INFO_LIST: - fill_source_info(client, reply, &impl->default_source); - fill_source_info(client, reply, &impl->default_monitor); + fill_source_info(client, info.reply, &impl->default_source); + fill_source_info(client, info.reply, &impl->default_monitor); break; case COMMAND_GET_SINK_INPUT_INFO_LIST: case COMMAND_GET_SOURCE_OUTPUT_INFO_LIST: @@ -2370,7 +2483,10 @@ static int do_get_info_list(struct client *client, uint32_t command, uint32_t ta default: return -ENOTSUP; } - return send_message(client, reply); + if (list_func) + pw_manager_for_each_object(client->manager, list_func, &info); + + return send_message(client, info.reply); } static int do_set_stream_buffer_attr(struct client *client, uint32_t command, uint32_t tag, struct message *m) @@ -2664,6 +2780,8 @@ static void client_free(struct client *client) message_free(client, msg, true, true); spa_list_consume(msg, &client->out_messages, link) message_free(client, msg, true, true); + if (client->manager) + pw_manager_destroy(client->manager); if (client->core) { client->disconnecting = true; pw_core_disconnect(client->core); @@ -2906,6 +3024,7 @@ on_connect(void *data, int fd, uint32_t mask) pw_map_init(&client->streams, 16, 16); spa_list_init(&client->free_messages); spa_list_init(&client->out_messages); + spa_list_init(&client->operations); client->props = pw_properties_new( PW_KEY_CLIENT_API, "pipewire-pulse",