A router might want, for example, to move streams elsewhere when the current sink or source disappears due to a profile switch. --- src/modules/bluetooth/module-bluetooth-device.c | 8 ++++---- src/modules/bluetooth/module-bluetooth-policy.c | 2 +- src/modules/dbus/iface-card.c | 2 +- src/modules/module-card-restore.c | 2 +- src/modules/module-switch-on-port-available.c | 2 +- src/pulsecore/card.c | 13 ++++++++++++- src/pulsecore/card.h | 2 +- src/pulsecore/cli-command.c | 2 +- src/pulsecore/protocol-native.c | 2 +- src/pulsecore/router.c | 11 +++++++++++ src/pulsecore/router.h | 13 +++++++++++++ 11 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 34745fd..5562f6d 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -537,7 +537,7 @@ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t o pa_log_debug("Switching the profile to off due to IO thread failure."); - pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); + pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false, false) >= 0); break; } } @@ -1809,7 +1809,7 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa pa_assert(u); if (t == u->transport && t->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) - pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); + pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false, false) >= 0); if (t->device == u->device) handle_transport_state_change(u, t); @@ -2054,7 +2054,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { off: stop_thread(u); - pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); + pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false, false) >= 0); return -PA_ERR_IO; } @@ -2561,7 +2561,7 @@ int pa__init(pa_module *m) { off: stop_thread(u); - pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); + pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false, false) >= 0); return 0; diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index a5e9985..a7733e0 100644 --- a/src/modules/bluetooth/module-bluetooth-policy.c +++ b/src/modules/bluetooth/module-bluetooth-policy.c @@ -195,7 +195,7 @@ static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_prof pa_log_debug("Setting card '%s' to profile '%s'", card->name, selected_profile->name); - if (pa_card_set_profile(card, selected_profile, false) != 0) + if (pa_card_set_profile(card, selected_profile, false, false) != 0) pa_log_warn("Could not set profile '%s'", selected_profile->name); return PA_HOOK_OK; diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index b77a5e4..d4b080b 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -336,7 +336,7 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DB return; } - if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_profile(new_active), true)) < 0) { + if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_profile(new_active), true, false)) < 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_card_set_profile() failed with error code %i.", r); return; diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 3134067..cc55008 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -413,7 +413,7 @@ static pa_hook_result_t card_profile_added_callback(pa_core *c, pa_card_profile return PA_HOOK_OK; if (pa_safe_streq(entry->profile, profile->name)) { - if (pa_card_set_profile(profile->card, profile, true) >= 0) + if (pa_card_set_profile(profile->card, profile, true, false) >= 0) pa_log_info("Restored profile '%s' for card %s.", profile->name, profile->card->name); } diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c index 2c7ad17..2ba5397 100644 --- a/src/modules/module-switch-on-port-available.c +++ b/src/modules/module-switch-on-port-available.c @@ -121,7 +121,7 @@ static int try_to_switch_profile(pa_device_port *port) { return -1; } - if (pa_card_set_profile(port->card, best_profile, false) != 0) { + if (pa_card_set_profile(port->card, best_profile, false, false) != 0) { pa_log_debug("Could not set profile %s", best_profile->name); return -1; } diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 8ca6e54..e61f6df 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -32,6 +32,7 @@ #include <pulsecore/log.h> #include <pulsecore/macro.h> +#include <pulsecore/router.h> #include <pulsecore/core-util.h> #include <pulsecore/namereg.h> #include <pulsecore/device-port.h> @@ -250,7 +251,7 @@ void pa_card_free(pa_card *c) { pa_xfree(c); } -int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) { +int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save, bool bypass_router) { int r; pa_assert(c); @@ -267,6 +268,16 @@ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) { return 0; } + if (!bypass_router && c->core->router) + /* pa_router_set_card_profile() will probably call + * pa_card_set_profile() with bypass_router=true (to avoid an infinite + * loop), so there will be a bit of recursion going on. The purpose of + * this is to allow the router to be in full control of all profile + * switching in the system. The router can do something before and/or + * after the profile switch, or even reject the profile switch request + * altogether. */ + return pa_router_set_card_profile(c->core->router, c, profile, save); + if ((r = c->set_profile(c, profile)) < 0) return r; diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index 5318150..e2132ee 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -115,7 +115,7 @@ void pa_card_free(pa_card *c); void pa_card_add_profile(pa_card *c, pa_card_profile *profile); -int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save); +int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save, bool bypass_router); int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 44b5d84..d390a74 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -1661,7 +1661,7 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b return -1; } - if (pa_card_set_profile(card, profile, true) < 0) { + if (pa_card_set_profile(card, profile, true, false) < 0) { pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p); return -1; } diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index e195ba8..dd38cc4 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -4736,7 +4736,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_ CHECK_VALIDITY(c->pstream, profile, tag, PA_ERR_NOENTITY); - if ((ret = pa_card_set_profile(card, profile, true)) < 0) { + if ((ret = pa_card_set_profile(card, profile, true, false)) < 0) { pa_pstream_send_error(c->pstream, tag, -ret); return; } diff --git a/src/pulsecore/router.c b/src/pulsecore/router.c index 5d51f66..509aef5 100644 --- a/src/pulsecore/router.c +++ b/src/pulsecore/router.c @@ -89,3 +89,14 @@ void pa_router_remove_node(pa_router *router, pa_node *node) { if (router->remove_node) router->remove_node(router, node); } + +int pa_router_set_card_profile(pa_router *router, pa_card *card, pa_card_profile *profile, bool save) { + pa_assert(router); + pa_assert(card); + pa_assert(profile); + + if (router->set_card_profile) + return router->set_card_profile(router, card, profile, save); + else + return pa_card_set_profile(card, profile, save, true); +} diff --git a/src/pulsecore/router.h b/src/pulsecore/router.h index e8e7546..e27bb4a 100644 --- a/src/pulsecore/router.h +++ b/src/pulsecore/router.h @@ -35,6 +35,17 @@ struct pa_router { /* Called when a node goes away. May be NULL. */ void (*remove_node)(pa_router *router, pa_node *node); + + /* Called when someone requests a card profile change. May be NULL, in + * which case the profile change will happen normally. If this callback is + * set, then the implementation is responsible for calling + * pa_card_set_profile() (with bypass_router=true to avoid an infinite + * loop), if it wants any profile change to happen. + * + * If the implementation wants to report failure, then the return value + * must be a negative error code (meaning not just simple -1, but some of + * the PA_ERR_* constants). */ + int (*set_card_profile)(pa_router *router, pa_card *card, pa_card_profile *profile, bool save); }; pa_router *pa_router_new(pa_core *core); @@ -44,4 +55,6 @@ void pa_router_free(pa_router *router); void pa_router_add_node(pa_router *router, pa_node *node); void pa_router_remove_node(pa_router *router, pa_node *node); +int pa_router_set_card_profile(pa_router *router, pa_card *card, pa_card_profile *profile, bool save); + #endif -- 1.8.3.1