diff --git a/src/modules/module-protocol-pulse/client.h b/src/modules/module-protocol-pulse/client.h index 2c413e51c..81d790b51 100644 --- a/src/modules/module-protocol-pulse/client.h +++ b/src/modules/module-protocol-pulse/client.h @@ -62,8 +62,12 @@ struct client { struct pw_manager_object *metadata_schema_sm_settings; bool have_force_mono_audio; + bool default_force_mono_audio; + bool have_bluetooth_headset_autoswitch; + bool default_bluetooth_headset_autoswitch; struct pw_manager_object *metadata_sm_settings; bool force_mono_audio; + bool bluetooth_headset_autoswitch; uint32_t connect_tag; diff --git a/src/modules/module-protocol-pulse/defs.h b/src/modules/module-protocol-pulse/defs.h index 51e2453ff..c333f21f5 100644 --- a/src/modules/module-protocol-pulse/defs.h +++ b/src/modules/module-protocol-pulse/defs.h @@ -324,5 +324,6 @@ static inline uint32_t port_type_value(const char *port_type) #define METADATA_TARGET_NODE "target.node" #define METADATA_TARGET_OBJECT "target.object" #define METADATA_FEATURES_AUDIO_MONO "node.features.audio.mono" +#define METADATA_BLUETOOTH_HEADSET_AUTOSWITCH "bluetooth.autoswitch-to-headset-profile" #endif /* PULSE_SERVER_DEFS_H */ diff --git a/src/modules/module-protocol-pulse/message-handler.c b/src/modules/module-protocol-pulse/message-handler.c index 48c7c7be3..835a1d303 100644 --- a/src/modules/module-protocol-pulse/message-handler.c +++ b/src/modules/module-protocol-pulse/message-handler.c @@ -110,12 +110,57 @@ static int core_object_force_mono_output(struct client *client, const char *para if (spa_streq(params, "true")) { ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "true"); + client->force_mono_audio = true; } else if (spa_streq(params, "false")) { ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, METADATA_FEATURES_AUDIO_MONO, "Spa:String:JSON", "false"); + client->force_mono_audio = false; } else if (spa_streq(params, "null")) { ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, METADATA_FEATURES_AUDIO_MONO, NULL, NULL); + client->force_mono_audio = client->default_force_mono_audio; + } else { + fprintf(response, "Value must be true, false, or clear"); + return -EINVAL; + } + + if (ret < 0) + fprintf(response, "Could not set metadata: %s", spa_strerror(ret)); + else + fprintf(response, "%s", params); + + return ret; + } +} + +static int core_object_bluetooth_headset_autoswitch(struct client *client, const char *params, FILE *response) +{ + if (!client->have_bluetooth_headset_autoswitch) { + /* Not supported, return a null value to indicate that */ + fprintf(response, "null"); + return 0; + } + + if (!params || params[0] == '\0') { + /* No parameter => query the current value */ + fprintf(response, "%s", client->bluetooth_headset_autoswitch ? "true" : "false"); + return 0; + } else { + /* The caller is trying to set a value or clear with a null */ + int ret; + + if (spa_streq(params, "true")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "true"); + client->bluetooth_headset_autoswitch = true; + } else if (spa_streq(params, "false")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, "Spa:String:JSON", "false"); + client->bluetooth_headset_autoswitch = false; + } else if (spa_streq(params, "null")) { + ret = pw_manager_set_metadata(client->manager, client->metadata_sm_settings, PW_ID_CORE, + METADATA_BLUETOOTH_HEADSET_AUTOSWITCH, NULL, NULL); + client->bluetooth_headset_autoswitch = client->default_bluetooth_headset_autoswitch; } else { fprintf(response, "Value must be true, false, or clear"); return -EINVAL; @@ -138,14 +183,15 @@ static int core_object_message_handler(struct client *client, struct pw_manager_ fprintf(response, "/core []\n" "available commands:\n" - " help this help\n" - " list-handlers show available object handlers\n" - " pipewire-pulse:malloc-info show malloc_info\n" - " pipewire-pulse:malloc-trim run malloc_trim\n" - " pipewire-pulse:log-level update log level with \n" - " pipewire-pulse:list-modules list all module names\n" - " pipewire-pulse:describe-module describe module info for \n" - " pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs" + " help this help\n" + " list-handlers show available object handlers\n" + " pipewire-pulse:malloc-info show malloc_info\n" + " pipewire-pulse:malloc-trim run malloc_trim\n" + " pipewire-pulse:log-level update log level with \n" + " pipewire-pulse:list-modules list all module names\n" + " pipewire-pulse:describe-module describe module info for \n" + " pipewire-pulse:force-mono-output force mono mixdown on all hardware outputs\n" + " pipewire-pulse:bluetooth-headset-autoswitch use bluetooth headset mic if available" ); } else if (spa_streq(message, "list-handlers")) { bool first = true; @@ -208,6 +254,8 @@ static int core_object_message_handler(struct client *client, struct pw_manager_ } } else if (spa_streq(message, "pipewire-pulse:force-mono-output")) { return core_object_force_mono_output(client, params, response); + } else if (spa_streq(message, "pipewire-pulse:bluetooth-headset-autoswitch")) { + return core_object_bluetooth_headset_autoswitch(client, params, response); } else { return -ENOSYS; } diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index c22499718..59610ef57 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -973,12 +973,33 @@ static void manager_metadata(void *data, struct pw_manager_object *o, if (subject == PW_ID_CORE && o == client->metadata_routes) client_update_routes(client, key, value); if (subject == PW_ID_CORE && o == client->metadata_schema_sm_settings) { - if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) + char default_[16]; + + if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) { client->have_force_mono_audio = true; + + if (spa_json_str_object_find(value, strlen(value), + "default", default_, sizeof(default_)) < 0) + client->default_force_mono_audio = false; + else + client->default_force_mono_audio = spa_streq(default_, "true"); + } + + if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH)) { + client->have_bluetooth_headset_autoswitch = true; + + if (spa_json_str_object_find(value, strlen(value), + "default", default_, sizeof(default_)) < 0) + client->default_bluetooth_headset_autoswitch = false; + else + client->default_bluetooth_headset_autoswitch = spa_streq(default_, "true"); + } } if (subject == PW_ID_CORE && o == client->metadata_sm_settings) { if (spa_streq(key, METADATA_FEATURES_AUDIO_MONO)) client->force_mono_audio = spa_streq(value, "true"); + if (spa_streq(key, METADATA_BLUETOOTH_HEADSET_AUTOSWITCH)) + client->bluetooth_headset_autoswitch = spa_streq(value, "true"); } }