-/* $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
#include <config.h>
#endif
-#include <unistd.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-
#include <pulse/xmalloc.h>
-#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
#include <pulsecore/core-util.h>
-#include <pulse/volume.h>
#include "module-volume-restore-symdef.h"
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Playback stream automatic volume restore module")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-#define WHITESPACE "\n\r \t"
-
-#define DEFAULT_VOLUME_TABLE_FILE ".pulse/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;
- 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(__FILE__": starting with empty ruleset.");
- ret = 0;
- } else
- pa_log(__FILE__": 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(__FILE__": 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(__FILE__": 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(__FILE__": 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(__FILE__": 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)
- 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 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 (((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) && si->sample_spec.channels == r->volume.channels) {
- pa_log_info(__FILE__": Restoring volume for <%s>", r->name);
- pa_sink_input_set_volume(si, &r->volume);
- } else if (!pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
- pa_log_info(__FILE__": Saving volume for <%s>", r->name);
- r->volume = *pa_sink_input_get_volume(si);
- u->modified = 1;
- }
-
- } else {
- pa_log_info(__FILE__": 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;
- }
-}
-
-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(__FILE__": Failed to parse module arguments");
+ 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.");
- u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u);
+ 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);
+
+ 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);
+ return -1;
}
-
-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->hashmap) {
-
- if (u->modified)
- save_rules(u);
-
- pa_hashmap_free(u->hashmap, free_func, NULL);
- }
-
- pa_xfree(u->table_file);
- pa_xfree(u);
-}
-
-