diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c index de9c87092..546b39cb1 100644 --- a/src/modules/module-switch-on-port-available.c +++ b/src/modules/module-switch-on-port-available.c @@ -26,11 +26,29 @@ #include #include #include +#include + +/* Ignore HDMI devices by default. HDMI monitors don't necessarily have audio + * output on them, and even if they do, waking up from sleep or changing + * monitor resolution may appear as a plugin event, which causes trouble if the + * user doesn't want to use the monitor for audio. */ +#define DEFAULT_BLACKLIST_PORTS "hdmi" +#define DEFAULT_BLACKLIST_PROFILES "hdmi" PA_MODULE_AUTHOR("David Henningsson"); PA_MODULE_DESCRIPTION("Switches ports and profiles when devices are plugged/unplugged"); PA_MODULE_LOAD_ONCE(true); PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_USAGE( + "blacklist_ports= " + "blacklist_profiles= " +); + +static const char* const valid_modargs[] = { + "blacklist_ports", + "blacklist_profiles", + NULL, +}; struct card_info { struct userdata *userdata; @@ -44,6 +62,8 @@ struct card_info { struct userdata { pa_hashmap *card_infos; /* pa_card -> struct card_info */ + char *blacklist_ports; + char *blacklist_profiles; }; static void card_info_new(struct userdata *u, pa_card *card) { @@ -296,9 +316,13 @@ static void switch_from_port(pa_device_port *port, struct port_pointers pp) { } -static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) { +static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, struct userdata *u) { struct port_pointers pp = find_port_pointers(port); + pa_assert(c); + pa_assert(port); + pa_assert(u); + if (!port->card) { pa_log_warn("Port %s does not have a card", port->name); return PA_HOOK_OK; @@ -312,6 +336,12 @@ static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port if (pa_safe_streq(pa_proplist_gets(port->card->proplist, PA_PROP_DEVICE_BUS), "bluetooth")) return PA_HOOK_OK; + /* Ignore ports matching the blacklist regex */ + if (u->blacklist_ports && (pa_match(u->blacklist_ports, port->name) > 0)) { + pa_log_info("Ignoring blacklisted port %s", port->name); + return PA_HOOK_OK; + } + switch (port->available) { case PA_AVAILABLE_UNKNOWN: /* If a port availability became unknown, let's see if it's part of @@ -387,6 +417,7 @@ static pa_card_profile *find_best_profile(pa_card *card) { static pa_hook_result_t card_profile_available_hook_callback(pa_core *c, pa_card_profile *profile, struct userdata *u) { pa_card *card; + pa_assert(u); pa_assert(profile); pa_assert_se(card = profile->card); @@ -401,6 +432,12 @@ static pa_hook_result_t card_profile_available_hook_callback(pa_core *c, pa_card return PA_HOOK_OK; } + /* Ignore p matching the blacklist regex */ + if (u->blacklist_profiles && (pa_match(u->blacklist_profiles, profile->name) > 0)) { + pa_log_info("Ignoring blacklisted card profile %s", profile->name); + return PA_HOOK_OK; + } + pa_log_debug("Active profile %s on card %s became unavailable, switching to another profile", profile->name, card->name); pa_card_set_profile(card, find_best_profile(card), false); @@ -408,7 +445,7 @@ static pa_hook_result_t card_profile_available_hook_callback(pa_core *c, pa_card } -static void handle_all_unavailable(pa_core *core) { +static void handle_all_unavailable(pa_core *core, struct userdata *u) { pa_card *card; uint32_t state; @@ -418,7 +455,7 @@ static void handle_all_unavailable(pa_core *core) { PA_HASHMAP_FOREACH(port, card->ports, state2) { if (port->available == PA_AVAILABLE_NO) - port_available_hook_callback(core, port, NULL); + port_available_hook_callback(core, port, u); } } } @@ -591,15 +628,49 @@ static pa_hook_result_t sink_port_changed_callback(pa_core *core, pa_sink *sink, } int pa__init(pa_module*m) { + pa_modargs *ma; struct userdata *u; pa_card *card; uint32_t idx; pa_assert(m); + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + return -1; + } + u = m->userdata = pa_xnew0(struct userdata, 1); u->card_infos = pa_hashmap_new(NULL, NULL); + u->blacklist_ports = pa_xstrdup(pa_modargs_get_value(ma, "blacklist_ports", DEFAULT_BLACKLIST_PORTS)); + + /* An empty string disables all blacklisting. */ + if (!*u->blacklist_ports) { + pa_xfree(u->blacklist_ports); + u->blacklist_ports = NULL; + } + + if (u->blacklist_ports != NULL && !pa_is_regex_valid(u->blacklist_ports)) { + pa_log_error("A port blacklist pattern was provided but is not a valid regex"); + pa_xfree(u->blacklist_ports); + goto fail; + } + + u->blacklist_profiles = pa_xstrdup(pa_modargs_get_value(ma, "blacklist_profiles", DEFAULT_BLACKLIST_PROFILES)); + + /* An empty string disables all blacklisting. */ + if (!*u->blacklist_profiles) { + pa_xfree(u->blacklist_profiles); + u->blacklist_profiles = NULL; + } + + if (u->blacklist_profiles != NULL && !pa_is_regex_valid(u->blacklist_profiles)) { + pa_log_error("A card profile blacklist pattern was provided but is not a valid regex"); + pa_xfree(u->blacklist_profiles); + goto fail; + } + PA_IDXSET_FOREACH(card, m->core->cards, idx) card_info_new(u, card); @@ -609,9 +680,9 @@ int pa__init(pa_module*m) { pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, NULL); pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], - PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, NULL); + PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, u); pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], - PA_HOOK_LATE, (pa_hook_cb_t) card_profile_available_hook_callback, NULL); + PA_HOOK_LATE, (pa_hook_cb_t) card_profile_available_hook_callback, u); pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u); pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_UNLINK], @@ -623,9 +694,19 @@ int pa__init(pa_module*m) { pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_changed_callback, NULL); - handle_all_unavailable(m->core); + handle_all_unavailable(m->core, u); + + pa_modargs_free(ma); return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; } void pa__done(pa_module *module) { @@ -642,5 +723,11 @@ void pa__done(pa_module *module) { pa_hashmap_free(u->card_infos); + if (u->blacklist_ports) + pa_xfree(u->blacklist_ports); + + if (u->blacklist_profiles) + pa_xfree(u->blacklist_profiles); + pa_xfree(u); }