diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in index 6f7feb98a..776018f6a 100644 --- a/src/daemon/pipewire-pulse.conf.in +++ b/src/daemon/pipewire-pulse.conf.in @@ -63,6 +63,9 @@ context.exec = [ # ( flags = [ nofail ] ) pulse.cmd = [ { cmd = "load-module" args = "module-always-sink" flags = [ ] } + { cmd = "load-module" args = "module-device-manager" flags = [ ] } + { cmd = "load-module" args = "module-device-restore" flags = [ ] } + { cmd = "load-module" args = "module-stream-restore" flags = [ ] } #{ cmd = "load-module" args = "module-switch-on-connect" } #{ cmd = "load-module" args = "module-gsettings" flags = [ nofail ] } ] diff --git a/src/modules/meson.build b/src/modules/meson.build index 37cb854a9..e2478c965 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -342,9 +342,6 @@ pipewire_module_protocol_pulse_sources = [ 'module-protocol-pulse/collect.c', 'module-protocol-pulse/cmd.c', 'module-protocol-pulse/extension.c', - 'module-protocol-pulse/extensions/ext-device-manager.c', - 'module-protocol-pulse/extensions/ext-device-restore.c', - 'module-protocol-pulse/extensions/ext-stream-restore.c', 'module-protocol-pulse/format.c', 'module-protocol-pulse/manager.c', 'module-protocol-pulse/message.c', @@ -366,6 +363,8 @@ pipewire_module_protocol_pulse_sources = [ 'module-protocol-pulse/modules/module-alsa-source.c', 'module-protocol-pulse/modules/module-always-sink.c', 'module-protocol-pulse/modules/module-combine-sink.c', + 'module-protocol-pulse/modules/module-device-manager.c', + 'module-protocol-pulse/modules/module-device-restore.c', 'module-protocol-pulse/modules/module-echo-cancel.c', 'module-protocol-pulse/modules/module-jackdbus-detect.c', 'module-protocol-pulse/modules/module-ladspa-sink.c', @@ -384,6 +383,7 @@ pipewire_module_protocol_pulse_sources = [ 'module-protocol-pulse/modules/module-rtp-recv.c', 'module-protocol-pulse/modules/module-rtp-send.c', 'module-protocol-pulse/modules/module-simple-protocol-tcp.c', + 'module-protocol-pulse/modules/module-stream-restore.c', 'module-protocol-pulse/modules/module-switch-on-connect.c', 'module-protocol-pulse/modules/module-tunnel-sink.c', 'module-protocol-pulse/modules/module-tunnel-source.c', diff --git a/src/modules/module-protocol-pulse/defs.h b/src/modules/module-protocol-pulse/defs.h index 9abfda89e..8832f0aae 100644 --- a/src/modules/module-protocol-pulse/defs.h +++ b/src/modules/module-protocol-pulse/defs.h @@ -37,7 +37,6 @@ #define SCACHE_ENTRY_SIZE_MAX (1024*1024*16) #define MODULE_INDEX_MASK 0xfffffffu -#define MODULE_EXTENSION_FLAG (1u << 28) #define MODULE_FLAG (1u << 29) #define DEFAULT_SINK "@DEFAULT_SINK@" diff --git a/src/modules/module-protocol-pulse/extension.c b/src/modules/module-protocol-pulse/extension.c index 7d4195aee..8082d7d09 100644 --- a/src/modules/module-protocol-pulse/extension.c +++ b/src/modules/module-protocol-pulse/extension.c @@ -5,21 +5,45 @@ #include #include +#include "client.h" #include "defs.h" #include "extension.h" -#include "extensions/registry.h" +#include "message.h" +#include "module.h" -static const struct extension extensions[] = { - { "module-stream-restore", 0 | MODULE_EXTENSION_FLAG, do_extension_stream_restore, }, - { "module-device-restore", 1 | MODULE_EXTENSION_FLAG, do_extension_device_restore, }, - { "module-device-manager", 2 | MODULE_EXTENSION_FLAG, do_extension_device_manager, }, -}; - -const struct extension *extension_find(uint32_t index, const char *name) +static const struct extension *find_extension_command(struct module *module, uint32_t command) { - SPA_FOR_EACH_ELEMENT_VAR(extensions, ext) { - if (index == ext->index || spa_streq(name, ext->name)) - return ext; + uint32_t i; + + if (module->info->extension == NULL) + return NULL; + + for (i = 0; module->info->extension[i].name; i++) { + if (module->info->extension[i].command == command) + return &module->info->extension[i]; } return NULL; } + +int extension_process(struct module *module, struct client *client, uint32_t tag, struct message *m) +{ + uint32_t command; + const struct extension *ext; + int res; + + if ((res = message_get(m, + TAG_U32, &command, + TAG_INVALID)) < 0) + return -EPROTO; + + ext = find_extension_command(module, command); + if (ext == NULL) + return -ENOTSUP; + if (ext->process == NULL) + return -EPROTO; + + pw_log_info("client %p [%s]: %s %s tag:%u", + client, client->name, module->info->name, ext->name, tag); + + return ext->process(module, client, command, tag, m); +} diff --git a/src/modules/module-protocol-pulse/extension.h b/src/modules/module-protocol-pulse/extension.h index 038807955..5736fe5e6 100644 --- a/src/modules/module-protocol-pulse/extension.h +++ b/src/modules/module-protocol-pulse/extension.h @@ -9,19 +9,15 @@ struct client; struct message; - -struct extension_sub { - const char *name; - uint32_t command; - int (*process)(struct client *client, uint32_t command, uint32_t tag, struct message *m); -}; +struct module; struct extension { const char *name; - uint32_t index; - int (*process)(struct client *client, uint32_t tag, struct message *m); + uint32_t command; + int (*process)(struct module *module, struct client *client, uint32_t command, + uint32_t tag, struct message *m); }; -const struct extension *extension_find(uint32_t index, const char *name); +int extension_process(struct module *module, struct client *client, uint32_t tag, struct message *m); #endif /* PULSE_SERVER_EXTENSION_H */ diff --git a/src/modules/module-protocol-pulse/extensions/ext-device-manager.c b/src/modules/module-protocol-pulse/extensions/ext-device-manager.c deleted file mode 100644 index 2ba080ec8..000000000 --- a/src/modules/module-protocol-pulse/extensions/ext-device-manager.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -#include "registry.h" - -int do_extension_device_manager(struct client *client, uint32_t tag, struct message *m) -{ - return -ENOTSUP; -} diff --git a/src/modules/module-protocol-pulse/extensions/registry.h b/src/modules/module-protocol-pulse/extensions/registry.h deleted file mode 100644 index ab9faf734..000000000 --- a/src/modules/module-protocol-pulse/extensions/registry.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef PIPEWIRE_PULSE_EXTENSION_REGISTRY_H -#define PIPEWIRE_PULSE_EXTENSION_REGISTRY_H - -#include - -struct client; -struct message; - -int do_extension_stream_restore(struct client *client, uint32_t tag, struct message *m); -int do_extension_device_restore(struct client *client, uint32_t tag, struct message *m); -int do_extension_device_manager(struct client *client, uint32_t tag, struct message *m); - -#endif /* PIPEWIRE_PULSE_EXTENSION_REGISTRY_H */ diff --git a/src/modules/module-protocol-pulse/module.c b/src/modules/module-protocol-pulse/module.c index 6a31f626c..cc6f2ffa9 100644 --- a/src/modules/module-protocol-pulse/module.c +++ b/src/modules/module-protocol-pulse/module.c @@ -375,3 +375,19 @@ error_free: return NULL; } + +struct module *module_lookup(struct impl *impl, uint32_t index, const char *name) +{ + union pw_map_item *item; + + if (index != SPA_ID_INVALID) + return pw_map_lookup(&impl->modules, index); + + pw_array_for_each(item, &impl->modules.items) { + struct module *m = item->data; + if (!pw_map_item_is_free(item) && + spa_streq(m->info->name, name)) + return m; + } + return NULL; +} diff --git a/src/modules/module-protocol-pulse/module.h b/src/modules/module-protocol-pulse/module.h index 39e829d3c..d47d1f87c 100644 --- a/src/modules/module-protocol-pulse/module.h +++ b/src/modules/module-protocol-pulse/module.h @@ -13,6 +13,9 @@ struct module; struct pw_properties; +struct client; +struct message; +struct extension; struct module_info { const char *name; @@ -23,6 +26,7 @@ struct module_info { int (*load) (struct module *module); int (*unload) (struct module *module); + const struct extension *extension; const char* const *valid_args; const struct spa_dict *properties; size_t data_size; @@ -64,6 +68,8 @@ int module_load(struct module *module); int module_unload(struct module *module); void module_schedule_unload(struct module *module); +struct module *module_lookup(struct impl *impl, uint32_t index, const char *name); + void module_add_listener(struct module *module, struct spa_hook *listener, const struct module_events *events, void *data); diff --git a/src/modules/module-protocol-pulse/modules/module-device-manager.c b/src/modules/module-protocol-pulse/modules/module-device-manager.c new file mode 100644 index 000000000..d07fbd21e --- /dev/null +++ b/src/modules/module-protocol-pulse/modules/module-device-manager.c @@ -0,0 +1,63 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include + +#include "../module.h" + +/** \page page_pulse_module_device_manager Device manager extension + * + * ## Module Name + * + * `module-device-manager` + * + * ## Module Options + * + * @pulse_module_options@ + */ + +static const char *const pulse_module_options = + "do_routing= " + "on_hotplug= " + "on_rescue="; + +#define NAME "device-manager" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct module_device_manager_data { + struct module *module; +}; + +static const struct spa_dict_item module_device_manager_info[] = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans " }, + { PW_KEY_MODULE_DESCRIPTION, "Keep track of devices (and their descriptions) both past and present and prioritise by role" }, + { PW_KEY_MODULE_USAGE, pulse_module_options }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + +static int module_device_manager_prepare(struct module * const module) +{ + PW_LOG_TOPIC_INIT(mod_topic); + + struct module_device_manager_data * const data = module->user_data; + data->module = module; + + return 0; +} + +static int module_device_manager_load(struct module *module) +{ + return 0; +} + +DEFINE_MODULE_INFO(module_device_manager) = { + .name = "module-device-manager", + .load_once = true, + .prepare = module_device_manager_prepare, + .load = module_device_manager_load, + .properties = &SPA_DICT_INIT_ARRAY(module_device_manager_info), + .data_size = sizeof(struct module_device_manager_data), +}; diff --git a/src/modules/module-protocol-pulse/extensions/ext-device-restore.c b/src/modules/module-protocol-pulse/modules/module-device-restore.c similarity index 74% rename from src/modules/module-protocol-pulse/extensions/ext-device-restore.c rename to src/modules/module-protocol-pulse/modules/module-device-restore.c index a67ab1e17..ddb0e6450 100644 --- a/src/modules/module-protocol-pulse/extensions/ext-device-restore.c +++ b/src/modules/module-protocol-pulse/modules/module-device-restore.c @@ -1,7 +1,22 @@ /* PipeWire */ -/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */ /* SPDX-License-Identifier: MIT */ +#include + +#include "../module.h" + +/** \page page_pulse_module_device_restore Device restore extension + * + * ## Module Name + * + * `module-device-restore` + * + * ## Module Options + * + * @pulse_module_options@ + */ + #define EXT_DEVICE_RESTORE_VERSION 1 #include @@ -30,16 +45,33 @@ #include "../message.h" #include "../reply.h" #include "../volume.h" -#include "registry.h" -PW_LOG_TOPIC_EXTERN(pulse_ext_dev_restore); -#undef PW_LOG_TOPIC_DEFAULT -#define PW_LOG_TOPIC_DEFAULT pulse_ext_dev_restore +static const char *const pulse_module_options = + "restore_port= " + "restore_volume= " + "restore_muted= " + "restore_formats="; + +#define NAME "device-restore" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct module_device_restore_data { + struct module *module; +}; + +static const struct spa_dict_item module_device_restore_info[] = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans " }, + { PW_KEY_MODULE_DESCRIPTION, "Automatically restore the volume/mute state of devices" }, + { PW_KEY_MODULE_USAGE, pulse_module_options }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; #define DEVICE_TYPE_SINK 0 #define DEVICE_TYPE_SOURCE 1 -static int do_extension_device_restore_test(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_device_restore_test(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct message *reply; @@ -51,7 +83,7 @@ static int do_extension_device_restore_test(struct client *client, uint32_t comm return client_queue_message(client, reply); } -static int do_extension_device_restore_subscribe(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_device_restore_subscribe(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { return reply_simple_ack(client, tag); } @@ -103,7 +135,7 @@ static int do_sink_read_format(void *data, struct pw_manager_object *o) return 0; } -static int do_extension_device_restore_read_formats_all(struct client *client, +static int do_extension_device_restore_read_formats_all(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct pw_manager *manager = client->manager; @@ -118,7 +150,7 @@ static int do_extension_device_restore_read_formats_all(struct client *client, return client_queue_message(client, data.reply); } -static int do_extension_device_restore_read_formats(struct client *client, +static int do_extension_device_restore_read_formats(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct pw_manager *manager = client->manager; @@ -217,7 +249,7 @@ static int set_node_codecs(struct pw_manager_object *o, uint32_t n_codecs, uint3 } -static int do_extension_device_restore_save_formats(struct client *client, +static int do_extension_device_restore_save_formats(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct pw_manager *manager = client->manager; @@ -285,32 +317,37 @@ static int do_extension_device_restore_save_formats(struct client *client, return reply_simple_ack(client, tag); } -static const struct extension_sub ext_device_restore[] = { +static const struct extension module_device_restore_extension[] = { { "TEST", 0, do_extension_device_restore_test, }, { "SUBSCRIBE", 1, do_extension_device_restore_subscribe, }, { "EVENT", 2, }, { "READ_FORMATS_ALL", 3, do_extension_device_restore_read_formats_all, }, { "READ_FORMATS", 4, do_extension_device_restore_read_formats, }, { "SAVE_FORMATS", 5, do_extension_device_restore_save_formats, }, + { NULL }, }; -int do_extension_device_restore(struct client *client, uint32_t tag, struct message *m) +static int module_device_restore_prepare(struct module * const module) { - uint32_t command; - int res; + PW_LOG_TOPIC_INIT(mod_topic); - if ((res = message_get(m, - TAG_U32, &command, - TAG_INVALID)) < 0) - return -EPROTO; + struct module_device_restore_data * const data = module->user_data; + data->module = module; - if (command >= SPA_N_ELEMENTS(ext_device_restore)) - return -ENOTSUP; - if (ext_device_restore[command].process == NULL) - return -EPROTO; - - pw_log_info("client %p [%s]: EXT_DEVICE_RESTORE_%s tag:%u", - client, client->name, ext_device_restore[command].name, tag); - - return ext_device_restore[command].process(client, command, tag, m); + return 0; } + +static int module_device_restore_load(struct module *module) +{ + return 0; +} + +DEFINE_MODULE_INFO(module_device_restore) = { + .name = "module-device-restore", + .load_once = true, + .prepare = module_device_restore_prepare, + .load = module_device_restore_load, + .extension = module_device_restore_extension, + .properties = &SPA_DICT_INIT_ARRAY(module_device_restore_info), + .data_size = sizeof(struct module_device_restore_data), +}; diff --git a/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c b/src/modules/module-protocol-pulse/modules/module-stream-restore.c similarity index 73% rename from src/modules/module-protocol-pulse/extensions/ext-stream-restore.c rename to src/modules/module-protocol-pulse/modules/module-stream-restore.c index f20821246..931165fb8 100644 --- a/src/modules/module-protocol-pulse/extensions/ext-stream-restore.c +++ b/src/modules/module-protocol-pulse/modules/module-stream-restore.c @@ -1,7 +1,46 @@ /* PipeWire */ -/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */ +/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans */ /* SPDX-License-Identifier: MIT */ +#include + +#include "../module.h" + +/** \page page_pulse_module_stream_restore Stream restore extension + * + * ## Module Name + * + * `module-stream-restore` + * + * ## Module Options + * + * @pulse_module_options@ + */ + +static const char *const pulse_module_options = + "restore_device= " + "restore_volume= " + "restore_muted= " + "on_hotplug= " + "on_rescue= " + "fallback_table="; + +#define NAME "stream-restore" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +struct module_stream_restore_data { + struct module *module; +}; + +static const struct spa_dict_item module_stream_restore_info[] = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans " }, + { PW_KEY_MODULE_DESCRIPTION, "Automatically restore the volume/mute/device state of streams" }, + { PW_KEY_MODULE_USAGE, pulse_module_options }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + #define EXT_STREAM_RESTORE_VERSION 1 #include @@ -26,13 +65,12 @@ #include "../remap.h" #include "../reply.h" #include "../volume.h" -#include "registry.h" PW_LOG_TOPIC_EXTERN(pulse_ext_stream_restore); #undef PW_LOG_TOPIC_DEFAULT #define PW_LOG_TOPIC_DEFAULT pulse_ext_stream_restore -static int do_extension_stream_restore_test(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_stream_restore_test(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct message *reply; @@ -119,7 +157,7 @@ static int key_to_name(const char *key, char *name, size_t maxlen) } -static int do_extension_stream_restore_read(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_stream_restore_read(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct message *reply; const struct spa_dict_item *item; @@ -194,7 +232,7 @@ static int do_extension_stream_restore_read(struct client *client, uint32_t comm return client_queue_message(client, reply); } -static int do_extension_stream_restore_write(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_stream_restore_write(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { int res; uint32_t mode; @@ -269,42 +307,47 @@ static int do_extension_stream_restore_write(struct client *client, uint32_t com return reply_simple_ack(client, tag); } -static int do_extension_stream_restore_delete(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_stream_restore_delete(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { return reply_simple_ack(client, tag); } -static int do_extension_stream_restore_subscribe(struct client *client, uint32_t command, uint32_t tag, struct message *m) +static int do_extension_stream_restore_subscribe(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m) { return reply_simple_ack(client, tag); } -static const struct extension_sub ext_stream_restore[] = { +static const struct extension module_stream_restore_extension[] = { { "TEST", 0, do_extension_stream_restore_test, }, { "READ", 1, do_extension_stream_restore_read, }, { "WRITE", 2, do_extension_stream_restore_write, }, { "DELETE", 3, do_extension_stream_restore_delete, }, { "SUBSCRIBE", 4, do_extension_stream_restore_subscribe, }, { "EVENT", 5, }, + { NULL, }, }; -int do_extension_stream_restore(struct client *client, uint32_t tag, struct message *m) +static int module_stream_restore_prepare(struct module * const module) { - uint32_t command; - int res; + PW_LOG_TOPIC_INIT(mod_topic); - if ((res = message_get(m, - TAG_U32, &command, - TAG_INVALID)) < 0) - return -EPROTO; + struct module_stream_restore_data * const data = module->user_data; + data->module = module; - if (command >= SPA_N_ELEMENTS(ext_stream_restore)) - return -ENOTSUP; - if (ext_stream_restore[command].process == NULL) - return -EPROTO; - - pw_log_info("client %p [%s]: EXT_STREAM_RESTORE_%s tag:%u", - client, client->name, ext_stream_restore[command].name, tag); - - return ext_stream_restore[command].process(client, command, tag, m); + return 0; } + +static int module_stream_restore_load(struct module *module) +{ + return 0; +} + +DEFINE_MODULE_INFO(module_stream_restore) = { + .name = "module-stream-restore", + .load_once = true, + .prepare = module_stream_restore_prepare, + .load = module_stream_restore_load, + .extension = module_stream_restore_extension, + .properties = &SPA_DICT_INIT_ARRAY(module_stream_restore_info), + .data_size = sizeof(struct module_stream_restore_data), +}; diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 977b61711..8bbd888bf 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -4196,7 +4196,7 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st if (command == COMMAND_GET_MODULE_INFO && (sel.index & MODULE_FLAG) != 0) { struct module *module; - module = pw_map_lookup(&impl->modules, sel.index & MODULE_INDEX_MASK); + module = module_lookup(impl, sel.index & MODULE_INDEX_MASK, NULL); if (module == NULL) goto error_noentity; fill_ext_module_info(client, reply, module); @@ -4564,9 +4564,10 @@ static int do_update_stream_sample_rate(struct client *client, uint32_t command, static int do_extension(struct client *client, uint32_t command, uint32_t tag, struct message *m) { + struct impl *impl = client->impl; uint32_t index; const char *name; - const struct extension *ext; + struct module *module; if (message_get(m, TAG_U32, &index, @@ -4581,11 +4582,11 @@ static int do_extension(struct client *client, uint32_t command, uint32_t tag, s (index != SPA_ID_INVALID && name != NULL)) return -EINVAL; - ext = extension_find(index, name); - if (ext == NULL) + module = module_lookup(impl, index, name); + if (module == NULL) return -ENOENT; - return ext->process(client, tag, m); + return extension_process(module, client, tag, m); } static int do_set_profile(struct client *client, uint32_t command, uint32_t tag, struct message *m) @@ -5069,7 +5070,7 @@ static int do_unload_module(struct client *client, uint32_t command, uint32_t ta if ((module_index & MODULE_FLAG) == 0) return -EPERM; - module = pw_map_lookup(&impl->modules, module_index & MODULE_INDEX_MASK); + module = module_lookup(impl, module_index & MODULE_INDEX_MASK, NULL); if (module == NULL) return -ENOENT;