X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/521d15babb5be8f3ce47d2e73b1b3470c71159ba..1cda71725240bd4911f0f34c5d384b3966f06369:/src/modules/module-volume-restore.c diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 59c47743..cd397e27 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -1,18 +1,18 @@ -/* $Id$ */ - /*** This file is part of PulseAudio. - + + Copyright 2006 Lennart Poettering + PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, + by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 @@ -23,371 +23,63 @@ #include #endif -#include -#include -#include -#include -#include -#include -#include -#include - #include -#include #include -#include #include #include -#include -#include #include -#include #include "module-volume-restore-symdef.h" -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_DESCRIPTION("Automatically restore volume of playback streams") -PA_MODULE_USAGE("table=") -PA_MODULE_VERSION(PACKAGE_VERSION) - -#define WHITESPACE "\n\r \t" - -#define DEFAULT_VOLUME_TABLE_FILE "volume.table" +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Compatibility module"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); +PA_MODULE_DEPRECATED("Please use module-stream-restore instead of module-volume-restore!"); static const char* const valid_modargs[] = { "table", + "restore_device", + "restore_volume", NULL, }; -struct rule { - char* name; - pa_cvolume volume; -}; - -struct userdata { - pa_hashmap *hashmap; - pa_subscription *subscription; - pa_hook_slot *hook_slot; - int modified; - char *table_file; -}; - -static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) { - char *p; - long k; - unsigned i; - - assert(s); - assert(v); - - if (!isdigit(*s)) - return NULL; - - k = strtol(s, &p, 0); - if (k <= 0 || k > PA_CHANNELS_MAX) - return NULL; - - v->channels = (unsigned) k; - - for (i = 0; i < v->channels; i++) { - p += strspn(p, WHITESPACE); - - if (!isdigit(*p)) - return NULL; - - k = strtol(p, &p, 0); - - if (k < PA_VOLUME_MUTED) - return NULL; - - v->values[i] = (pa_volume_t) k; - } - - if (*p != 0) - return NULL; - - return v; -} - -static int load_rules(struct userdata *u) { - FILE *f; - int n = 0; - int ret = -1; - char buf_name[256], buf_volume[256]; - char *ln = buf_name; - - f = u->table_file ? - fopen(u->table_file, "r") : - pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r"); - - if (!f) { - if (errno == ENOENT) { - pa_log_info("starting with empty ruleset."); - ret = 0; - } else - pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno)); - - goto finish; - } - - pa_lock_fd(fileno(f), 1); - - while (!feof(f)) { - struct rule *rule; - pa_cvolume v; - - if (!fgets(ln, sizeof(buf_name), f)) - break; - - n++; - - pa_strip_nl(ln); - - if (ln[0] == '#' || !*ln ) - continue; - - if (ln == buf_name) { - ln = buf_volume; - continue; - } - - assert(ln == buf_volume); - - if (!parse_volume(buf_volume, &v)) { - pa_log("parse failure in %s:%u, stopping parsing", u->table_file, n); - goto finish; - } - - ln = buf_name; - - if (pa_hashmap_get(u->hashmap, buf_name)) { - pa_log("double entry in %s:%u, ignoring", u->table_file, n); - goto finish; - } - - rule = pa_xnew(struct rule, 1); - rule->name = pa_xstrdup(buf_name); - rule->volume = v; - - pa_hashmap_put(u->hashmap, rule->name, rule); - } - - if (ln == buf_volume) { - pa_log("invalid number of lines in %s.", u->table_file); - goto finish; - } - - ret = 0; - -finish: - if (f) { - pa_lock_fd(fileno(f), 0); - fclose(f); - } - - return ret; -} - -static int save_rules(struct userdata *u) { - FILE *f; - int ret = -1; - void *state = NULL; - struct rule *rule; - - f = u->table_file ? - fopen(u->table_file, "w") : - pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w"); - - if (!f) { - pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno)); - goto finish; - } - - pa_lock_fd(fileno(f), 1); - - while ((rule = pa_hashmap_iterate(u->hashmap, &state, NULL))) { - unsigned i; - - fprintf(f, "%s\n%u", rule->name, rule->volume.channels); - - for (i = 0; i < rule->volume.channels; i++) - fprintf(f, " %u", rule->volume.values[i]); - - fprintf(f, "\n"); - } - - ret = 0; - -finish: - if (f) { - pa_lock_fd(fileno(f), 0); - fclose(f); - } - - return ret; -} - -static char* client_name(pa_client *c) { - char *t, *e; - - if (!c->name || !c->driver) - return NULL; - - t = pa_sprintf_malloc("%s$%s", c->driver, c->name); - t[strcspn(t, "\n\r#")] = 0; - - if (!*t) { - pa_xfree(t); - return NULL; - } - - if ((e = strrchr(t, '('))) { - char *k = e + 1 + strspn(e + 1, "0123456789-"); - - /* Dirty trick: truncate all trailing parens with numbers in - * between, since they are usually used to identify multiple - * sessions of the same application, which is something we - * explicitly don't want. Besides other stuff this makes xmms - * with esound work properly for us. */ - - if (*k == ')' && *(k+1) == 0) - *e = 0; - } - - return t; -} - -static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { - struct userdata *u = userdata; - pa_sink_input *si; - struct rule *r; - char *name; - - assert(c); - assert(u); - - if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) && - t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE)) - return; - - if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) - return; - - if (!si->client || !(name = client_name(si->client))) - return; - - if ((r = pa_hashmap_get(u->hashmap, name))) { - pa_xfree(name); - - if (!pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) { - pa_log_info("Saving volume for <%s>", r->name); - r->volume = *pa_sink_input_get_volume(si); - u->modified = 1; - } - } else { - pa_log_info("Creating new entry for <%s>", name); - - r = pa_xnew(struct rule, 1); - r->name = name; - r->volume = *pa_sink_input_get_volume(si); - pa_hashmap_put(u->hashmap, r->name, r); - - u->modified = 1; - } -} - -static pa_hook_result_t hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) { - struct rule *r; - char *name; - - assert(data); - - if (!data->client || !(name = client_name(data->client))) - return PA_HOOK_OK; - - if ((r = pa_hashmap_get(u->hashmap, name))) { - - if (data->sample_spec_is_set && data->sample_spec.channels == r->volume.channels) { - pa_log_info("Restoring volume for <%s>", r->name); - pa_sink_input_new_data_set_volume(data, &r->volume); - } - } - - return PA_HOOK_OK; -} - -int pa__init(pa_core *c, pa_module*m) { +int pa__init(pa_module*m) { pa_modargs *ma = NULL; - struct userdata *u; - - assert(c); - assert(m); + bool restore_device = true, restore_volume = true; + pa_module *n; + char *t; + + pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } - u = pa_xnew(struct userdata, 1); - u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - u->subscription = NULL; - u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL)); - u->modified = 0; - - m->userdata = u; - - if (load_rules(u) < 0) + if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 || + pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) { + pa_log("restore_volume= and restore_device= expect boolean arguments"); goto fail; + } + + pa_log_warn("We will now load module-stream-restore. Please make sure to remove module-volume-restore from your configuration."); + + t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device)); + n = pa_module_load(m->core, "module-stream-restore", t); + pa_xfree(t); - u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscribe_callback, u); - u->hook_slot = pa_hook_connect(&c->hook_sink_input_new, (pa_hook_cb_t) hook_callback, u); + if (n) + pa_module_unload_request(m, true); pa_modargs_free(ma); - return 0; -fail: - pa__done(c, m); + return n ? 0 : -1; +fail: if (ma) pa_modargs_free(ma); - - return -1; -} -static void free_func(void *p, void *userdata) { - struct rule *r = p; - assert(r); - - pa_xfree(r->name); - pa_xfree(r); -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata* u; - - assert(c); - assert(m); - - if (!(u = m->userdata)) - return; - - if (u->subscription) - pa_subscription_free(u->subscription); - - if (u->hook_slot) - pa_hook_slot_free(u->hook_slot); - - if (u->hashmap) { - - if (u->modified) - save_rules(u); - - pa_hashmap_free(u->hashmap, free_func, NULL); - } - - pa_xfree(u->table_file); - pa_xfree(u); + return -1; } - -