[pulseaudio-discuss] [PATCH 6/6] device-restore: Add support for "factory settings".
Tanu Kaskinen
tanu.kaskinen at jollamobile.com
Thu Oct 25 10:37:26 PDT 2012
The idea of the feature is that a device vendor may want to initialize
device volumes to known good defaults. The factory settings are read
from a configuration that contains entries that look like this:
[Entry sink:foodevicename:barportname]
volume = front-left:70%,front-right:70%
The settings are written to the device-restore database during module
loading, but only if the database doesn't already contain an entry
with matching key, so user choices are not overwritten.
---
src/modules/module-device-restore.c | 143 ++++++++++++++++++++++++++++++++++-
1 file changed, 142 insertions(+), 1 deletion(-)
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index e334ff6..b1b2d38 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -53,6 +53,7 @@
#include <pulsecore/pstream-util.h>
#include <pulsecore/database.h>
#include <pulsecore/tagstruct.h>
+#include <pulsecore/conf-parser.h>
#include "module-device-restore-symdef.h"
@@ -64,15 +65,19 @@ PA_MODULE_USAGE(
"restore_port=<Save/restore port?> "
"restore_volume=<Save/restore volumes?> "
"restore_muted=<Save/restore muted states?> "
- "restore_formats=<Save/restore saved formats?>");
+ "restore_formats=<Save/restore saved formats?> "
+ "factory_settings=<file>");
#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+#define DEFAULT_FACTORY_SETTINGS_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "device-restore-factory-settings.conf"
+#define DEFAULT_FACTORY_SETTINGS_FILE_USER "device-restore-factory-settings.conf"
static const char* const valid_modargs[] = {
"restore_volume",
"restore_muted",
"restore_port",
"restore_formats",
+ "factory_settings",
NULL
};
@@ -131,6 +136,13 @@ struct perportentry {
pa_idxset *formats;
};
+struct factory_settings_entry {
+ char *key;
+ pa_cvolume volume;
+ pa_channel_map channel_map;
+ bool volume_valid;
+};
+
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct userdata *u = userdata;
@@ -1230,6 +1242,130 @@ static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_nati
return PA_HOOK_OK;
}
+static struct factory_settings_entry *factory_settings_entry_get(pa_hashmap *factory_settings, const char *section) {
+ struct factory_settings_entry *entry;
+
+ pa_assert(factory_settings);
+
+ if (!section || !pa_startswith(section, "Entry "))
+ return NULL;
+
+ section += 6;
+
+ if ((entry = pa_hashmap_get(factory_settings, section)))
+ return entry;
+
+ entry = pa_xnew0(struct factory_settings_entry, 1);
+ entry->key = pa_xstrdup(section);
+ pa_hashmap_put(factory_settings, entry->key, entry);
+
+ return entry;
+}
+
+static int parse_volume_cb(pa_config_parser_state *state) {
+ pa_hashmap *factory_settings;
+ struct factory_settings_entry *entry;
+
+ pa_assert(state);
+
+ factory_settings = state->userdata;
+
+ if (!(entry = factory_settings_entry_get(factory_settings, state->section))) {
+ pa_log("[%s:%u] volume option not expected in section '%s'.", state->filename, state->lineno, pa_strnull(state->section));
+ return -1;
+ }
+
+ if (pa_parse_cvolume(state->rvalue, &entry->volume, &entry->channel_map) < 0) {
+ pa_log("[%s:%u] failed to parse volume.", state->filename, state->lineno);
+ return -1;
+ }
+
+ entry->volume_valid = true;
+
+ return 0;
+}
+
+static int load_factory_settings(struct userdata *u, const char *filename) {
+ FILE *f = NULL;
+ char *fn;
+ int r;
+ pa_hashmap *factory_settings;
+ struct factory_settings_entry *entry;
+ void *state;
+
+ pa_config_item config_items[] = {
+ { "volume", parse_volume_cb, NULL, NULL },
+ { NULL, NULL, NULL, NULL }
+ };
+
+ if (!filename && !(f = pa_open_config_file(DEFAULT_FACTORY_SETTINGS_FILE, DEFAULT_FACTORY_SETTINGS_FILE_USER, NULL, &fn))) {
+ if (errno != ENOENT)
+ return -1;
+
+ return 0;
+ }
+
+ /* pa_config_parse() would open the file for us if we didn't open it
+ * ourselves, but if pa_config_parse() has to open the file, it will not
+ * return an error if the file is not found. That's supposedly useful in
+ * other situations, but we want to report failure if filename was
+ * explicitly configured and the file is not found, so we have to open the
+ * file ourselves. */
+ if (filename && !(f = pa_fopen_cloexec(filename, "r"))) {
+ pa_log("Failed to open '%s': %s", filename, pa_cstrerror(errno));
+ return -1;
+ }
+
+ if (!filename)
+ filename = fn;
+
+ factory_settings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if ((r = pa_config_parse(filename, f, config_items, NULL, factory_settings)) < 0)
+ goto finish;
+
+ PA_HASHMAP_FOREACH(entry, factory_settings, state) {
+ struct perportentry *ppe;
+
+ if (!entry->volume_valid)
+ continue;
+
+ ppe = perportentry_read_raw_name(u, entry->key);
+
+ if (!ppe || !ppe->volume_valid) {
+ if (!ppe)
+ ppe = perportentry_new(FALSE);
+
+ ppe->volume = entry->volume;
+ ppe->channel_map = entry->channel_map;
+ ppe->volume_valid = true;
+
+ perportentry_write_raw_name(u, entry->key, ppe);
+ pa_log_debug("Added factory settings to the database for entry %s.", entry->key);
+
+ /* Note that the device type doesn't matter when we give PA_INVALID_INDEX. */
+ trigger_save(u, PA_DEVICE_TYPE_SINK, PA_INVALID_INDEX);
+ }
+
+ perportentry_free(ppe);
+ }
+
+finish:
+
+ while ((entry = pa_hashmap_steal_first(factory_settings))) {
+ pa_xfree(entry->key);
+ pa_xfree(entry);
+ }
+
+ pa_hashmap_free(factory_settings, NULL, NULL);
+ pa_xfree(fn);
+
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
struct userdata *u;
@@ -1302,6 +1438,11 @@ int pa__init(pa_module*m) {
pa_log_info("Successfully opened database file '%s'.", fname);
pa_xfree(fname);
+ if (load_factory_settings(u, pa_modargs_get_value(ma, "factory_settings", NULL)) < 0) {
+ pa_log("Loading factory settings failed.");
+ goto fail;
+ }
+
PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
--
1.7.10.4
More information about the pulseaudio-discuss
mailing list