From e576bd924f95f0307572766195be2c1db2ab0ca9 Mon Sep 17 00:00:00 2001 From: "Igor V. Kovalenko" Date: Wed, 16 Dec 2020 00:35:05 +0300 Subject: [PATCH] card: handle sticky profile flag New card database entry version 5 for card profile is sticky flag. New messaging API handlers set-profile-sticky and get-profile-sticky. When card profile is sticky, always restore it even if it is unavailable, and prevent switching from it when ports become unavailable. Part-of: --- doc/messaging_api.txt | 12 ++++ src/modules/module-card-restore.c | 30 ++++++++-- src/modules/module-switch-on-port-available.c | 10 ++++ src/pulsecore/card.c | 85 ++++++++++++++++++++++++++- src/pulsecore/card.h | 1 + 5 files changed, 133 insertions(+), 5 deletions(-) diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt index d4833a4..ad0774f 100644 --- a/doc/messaging_api.txt +++ b/doc/messaging_api.txt @@ -35,3 +35,15 @@ Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez Message: switch-codec Parameters: "codec name" Return value: none + +Description: Set if card profile selection should be sticky instead of being automated +Object path: /card/ +Message: set-profile-sticky +Parameters: JSON "true" or "false" +Return value: none + +Description: Get if card profile selection should be sticky instead of being automated +Object path: /card/ +Message: get-profile-sticky +Parameters: None +Return value: JSON "true" or "false" diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index b35cf3e..08f8333 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -67,7 +67,7 @@ struct userdata { bool restore_bluetooth_profile; }; -#define ENTRY_VERSION 4 +#define ENTRY_VERSION 5 struct port_info { char *name; @@ -80,6 +80,7 @@ struct entry { pa_hashmap *ports; /* Port name -> struct port_info */ char *preferred_input_port; char *preferred_output_port; + bool profile_is_sticky; /* since version 5; must be restored together with profile name */ }; static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { @@ -153,7 +154,8 @@ static struct entry *entry_from_card(pa_card *card) { pa_assert(card); entry = entry_new(); - if (card->save_profile) + entry->profile_is_sticky = card->profile_is_sticky; + if (card->save_profile || entry->profile_is_sticky) entry->profile = pa_xstrdup(card->active_profile->name); PA_HASHMAP_FOREACH(port, card->ports, state) { @@ -189,6 +191,9 @@ static bool entrys_equal(struct entry *a, struct entry *b) { if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port)) return false; + if (a->profile_is_sticky != b->profile_is_sticky) + return false; + return true; } @@ -217,6 +222,8 @@ static bool entry_write(struct userdata *u, const char *name, const struct entry pa_tagstruct_puts(t, e->preferred_input_port); pa_tagstruct_puts(t, e->preferred_output_port); + pa_tagstruct_put_boolean(t, e->profile_is_sticky); + key.data = (char *) name; key.size = strlen(name); @@ -342,6 +349,14 @@ static struct entry* entry_read(struct userdata *u, const char *name) { e->preferred_output_port = pa_xstrdup(preferred_output_port); } + if (version >= 5) { + bool profile_is_sticky; + if (pa_tagstruct_get_boolean(t, &profile_is_sticky) < 0) + goto fail; + + e->profile_is_sticky = profile_is_sticky; + } + if (!pa_tagstruct_eof(t)) goto fail; @@ -437,11 +452,12 @@ static pa_hook_result_t card_profile_changed_callback(pa_core *c, pa_card *card, pa_assert(card); - if (!card->save_profile) + if (!card->save_profile && !card->profile_is_sticky) return PA_HOOK_OK; if ((entry = entry_read(u, card->name))) { pa_xfree(entry->profile); + entry->profile_is_sticky = card->profile_is_sticky; entry->profile = pa_xstrdup(card->active_profile->name); pa_log_info("Storing card profile for card %s.", card->name); } else { @@ -565,12 +581,18 @@ static pa_hook_result_t card_choose_initial_profile_callback(pa_core *core, pa_c goto finish; } + card->profile_is_sticky = e->profile_is_sticky; + pa_log_info("Profile '%s' was previously %s for card %s.", + e->profile, + card->profile_is_sticky ? "sticky" : "automatically selected", + card->name); + if (e->profile[0]) { pa_card_profile *profile; profile = pa_hashmap_get(card->profiles, e->profile); if (profile) { - if (profile->available != PA_AVAILABLE_NO) { + if (profile->available != PA_AVAILABLE_NO || card->profile_is_sticky) { pa_log_info("Restoring profile '%s' for card %s.", profile->name, card->name); pa_card_set_profile(card, profile, true); } else diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c index e60d785..de9c870 100644 --- a/src/modules/module-switch-on-port-available.c +++ b/src/modules/module-switch-on-port-available.c @@ -137,6 +137,11 @@ static int try_to_switch_profile(pa_device_port *port) { void *state; unsigned best_prio = 0; + if (port->card->profile_is_sticky) { + pa_log_info("Keeping sticky card profile '%s'", port->card->active_profile->name); + return -1; + } + pa_log_debug("Finding best profile for port %s, preferred = %s", port->name, pa_strnull(port->preferred_profile)); @@ -391,6 +396,11 @@ static pa_hook_result_t card_profile_available_hook_callback(pa_core *c, pa_card if (!pa_streq(profile->name, card->active_profile->name)) return PA_HOOK_OK; + if (card->profile_is_sticky) { + pa_log_info("Keeping sticky 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); diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 6989596..23b347b 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -28,14 +28,22 @@ #include #include +#include #include #include #include #include +#include #include #include "card.h" +static int card_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata); + +static char* make_message_handler_path(const char *name) { + return pa_sprintf_malloc("/card/%s", name); +} + const char *pa_available_to_string(pa_available_t available) { switch (available) { case PA_AVAILABLE_UNKNOWN: @@ -136,7 +144,8 @@ void pa_card_new_data_done(pa_card_new_data *data) { pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { pa_card *c; - const char *name; + const char *name, *tmp; + char *object_path, *description; void *state; pa_card_profile *profile; pa_device_port *port; @@ -186,6 +195,14 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { pa_device_init_icon(c->proplist, true); pa_device_init_intended_roles(c->proplist); + object_path = make_message_handler_path(c->name); + if (!(tmp = pa_proplist_gets(c->proplist, PA_PROP_DEVICE_DESCRIPTION))) + tmp = c->name; + description = pa_sprintf_malloc("Message handler for card \"%s\"", tmp); + pa_message_handler_register(c->core, object_path, description, card_message_handler, (void *) c); + pa_xfree(object_path); + pa_xfree(description); + return c; } @@ -220,6 +237,7 @@ void pa_card_choose_initial_profile(pa_card *card) { card->active_profile = best; card->save_profile = false; + card->profile_is_sticky = false; pa_log_info("%s: active_profile: %s", card->name, card->active_profile->name); /* Let policy modules override the default. */ @@ -239,6 +257,7 @@ void pa_card_put(pa_card *card) { void pa_card_free(pa_card *c) { pa_core *core; + char *object_path; pa_assert(c); pa_assert(c->core); @@ -253,6 +272,10 @@ void pa_card_free(pa_card *c) { pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); } + object_path = make_message_handler_path(c->name); + pa_message_handler_unregister(core, object_path); + pa_xfree(object_path); + pa_namereg_unregister(core, c->name); pa_assert(pa_idxset_isempty(c->sinks)); @@ -305,6 +328,25 @@ static void update_port_preferred_profile(pa_card *c) { pa_device_port_set_preferred_profile(source->active_port, profile_name_for_dir(c->active_profile, PA_DIRECTION_INPUT)); } +static int card_set_profile_is_sticky(pa_card *c, bool profile_is_sticky) { + pa_assert(c); + + if (c->profile_is_sticky == profile_is_sticky) + return 0; + + pa_log_debug("%s: profile_is_sticky: %s -> %s", + c->name, pa_yes_no(c->profile_is_sticky), pa_yes_no(profile_is_sticky)); + + c->profile_is_sticky = profile_is_sticky; + + if (c->linked) { + pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c); + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); + } + + return 0; +} + int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) { int r; @@ -423,3 +465,44 @@ int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) { return ret; } + +static int card_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) { + pa_card *c; + char *message_handler_path; + + pa_assert(c = (pa_card *) userdata); + pa_assert(message); + pa_assert(response); + + message_handler_path = make_message_handler_path(c->name); + + if (!object_path || !pa_streq(object_path, message_handler_path)) { + pa_xfree(message_handler_path); + return -PA_ERR_NOENTITY; + } + + pa_xfree(message_handler_path); + + if (pa_streq(message, "get-profile-sticky")) { + pa_json_encoder *encoder; + encoder = pa_json_encoder_new(); + + pa_json_encoder_add_element_bool(encoder, c->profile_is_sticky); + + *response = pa_json_encoder_to_string_free(encoder); + + return PA_OK; + } else if (pa_streq(message, "set-profile-sticky")) { + + if (!parameters || pa_json_object_get_type(parameters) != PA_JSON_TYPE_BOOL) { + pa_log_info("Card operation set-profile-sticky requires argument: \"true\" or \"false\""); + return -PA_ERR_INVALID; + } + + card_set_profile_is_sticky(c, pa_json_object_get_bool(parameters)); + + return PA_OK; + } + + return -PA_ERR_NOTIMPLEMENTED; +} diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index a11e33d..20f66aa 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -83,6 +83,7 @@ struct pa_card { pa_device_port *preferred_output_port; bool save_profile:1; + bool profile_is_sticky:1; pa_suspend_cause_t suspend_cause; -- 2.7.4