]> code.delx.au - pulseaudio/blobdiff - src/modules/module-volume-restore.c
bonjour-publish: Return ports in network byteorder
[pulseaudio] / src / modules / module-volume-restore.c
index addd937b56c60806a43eed3766381437e879cf89..cd397e270ea77a7084d6f7eaef190ca7c2a1eefa 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
 /***
   This file is part of PulseAudio.
 
@@ -7,7 +5,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   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
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #include <config.h>
 #endif
 
 #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 <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/source-output.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/namereg.h>
-#include <pulse/volume.h>
 
 #include "module-volume-restore-symdef.h"
 
 
 #include "module-volume-restore-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams")
-PA_MODULE_USAGE("table=<filename>")
-PA_MODULE_VERSION(PACKAGE_VERSION)
-
-#define WHITESPACE "\n\r \t"
-
-#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.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",
 
 static const char* const valid_modargs[] = {
     "table",
+    "restore_device",
+    "restore_volume",
     NULL,
 };
 
     NULL,
 };
 
-struct rule {
-    char* name;
-    int volume_is_set;
-    pa_cvolume volume;
-    char *sink;
-    char *source;
-};
-
-struct userdata {
-    pa_hashmap *hashmap;
-    pa_subscription *subscription;
-    pa_hook_slot *sink_input_hook_slot, *source_output_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], buf_sink[256], buf_source[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;
-        int v_is_set;
-
-        if (!fgets(ln, sizeof(buf_name), f))
-            break;
-
-        n++;
-
-        pa_strip_nl(ln);
-
-        if (ln[0] == '#')
-            continue;
-
-        if (ln == buf_name) {
-            ln = buf_volume;
-            continue;
-        }
-
-        if (ln == buf_volume) {
-            ln = buf_sink;
-            continue;
-        }
-
-        if (ln == buf_sink) {
-            ln = buf_source;
-            continue;
-        }
-
-        assert(ln == buf_source);
-
-        if (buf_volume[0]) {
-            if (!parse_volume(buf_volume, &v)) {
-                pa_log("parse failure in %s:%u, stopping parsing", u->table_file, n);
-                goto finish;
-            }
-
-            v_is_set = 1;
-        } else
-            v_is_set = 0;
-
-        ln = buf_name;
-
-        if (pa_hashmap_get(u->hashmap, buf_name)) {
-            pa_log("double entry in %s:%u, ignoring", u->table_file, n);
-            continue;
-        }
-
-        rule = pa_xnew(struct rule, 1);
-        rule->name = pa_xstrdup(buf_name);
-        if ((rule->volume_is_set = v_is_set))
-            rule->volume = v;
-        rule->sink = buf_sink[0] ? pa_xstrdup(buf_sink) : NULL;
-        rule->source = buf_source[0] ? pa_xstrdup(buf_source) : NULL;
-
-        pa_hashmap_put(u->hashmap, rule->name, rule);
-    }
-
-    if (ln != buf_name) {
-        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", rule->name);
-
-        if (rule->volume_is_set) {
-            fprintf(f, "%u", rule->volume.channels);
-
-            for (i = 0; i < rule->volume.channels; i++)
-                fprintf(f, " %u", rule->volume.values[i]);
-        }
-
-        fprintf(f, "\n%s\n%s\n",
-                rule->sink ? rule->sink : "",
-                rule->source ? rule->source : "");
-    }
-
-    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 = NULL;
-    pa_source_output *so = NULL;
-    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) &&
-        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
-        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
-        return;
-
-    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
-        if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
-            return;
-
-        if (!si->client || !(name = client_name(si->client)))
-            return;
-    } else {
-        assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
-
-        if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
-            return;
-
-        if (!so->client || !(name = client_name(so->client)))
-            return;
-    }
-
-    if ((r = pa_hashmap_get(u->hashmap, name))) {
-        pa_xfree(name);
-
-        if (si) {
-
-            if (!r->volume_is_set || !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);
-                r->volume_is_set = 1;
-                u->modified = 1;
-            }
-
-            if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
-                pa_log_info("Saving sink for <%s>", r->name);
-                pa_xfree(r->sink);
-                r->sink = pa_xstrdup(si->sink->name);
-                u->modified = 1;
-            }
-        } else {
-            assert(so);
-
-            if (!r->source || strcmp(so->source->name, r->source) != 0) {
-                pa_log_info("Saving source for <%s>", r->name);
-                pa_xfree(r->source);
-                r->source = pa_xstrdup(so->source->name);
-                u->modified = 1;
-            }
-        }
-
-    } else {
-        pa_log_info("Creating new entry for <%s>", name);
-
-        r = pa_xnew(struct rule, 1);
-        r->name = name;
-
-        if (si) {
-            r->volume = *pa_sink_input_get_volume(si);
-            r->volume_is_set = 1;
-            r->sink = pa_xstrdup(si->sink->name);
-            r->source = NULL;
-        } else {
-            assert(so);
-            r->volume_is_set = 0;
-            r->sink = NULL;
-            r->source = pa_xstrdup(so->source->name);
-        }
-
-        pa_hashmap_put(u->hashmap, r->name, r);
-        u->modified = 1;
-    }
-}
-
-static pa_hook_result_t sink_input_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 (r->volume_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);
-        }
-
-        if (!data->sink && r->sink) {
-            if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
-                pa_log_info("Restoring sink for <%s>", r->name);
-        }
-    }
-
-    pa_xfree(name);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_output_hook_callback(pa_core *c, pa_source_output_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->source && r->source) {
-            if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE, 1)))
-                pa_log_info("Restoring source for <%s>", r->name);
-        }
-    }
-
-    return PA_HOOK_OK;
-}
-
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
-    struct userdata *u;
+    bool restore_device = true, restore_volume = true;
+    pa_module *n;
+    char *t;
 
 
-    assert(m);
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments");
         goto fail;
     }
 
 
     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;
-    u->sink_input_hook_slot = u->source_output_hook_slot = NULL;
+    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;
+    }
 
 
-    m->userdata = u;
+    pa_log_warn("We will now load module-stream-restore. Please make sure to remove module-volume-restore from your configuration.");
 
 
-    if (load_rules(u) < 0)
-        goto fail;
+    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(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
-    u->sink_input_hook_slot = pa_hook_connect(&m->core->hook_sink_input_new, (pa_hook_cb_t) sink_input_hook_callback, u);
-    u->source_output_hook_slot = pa_hook_connect(&m->core->hook_source_output_new, (pa_hook_cb_t) source_output_hook_callback, u);
+    if (n)
+        pa_module_unload_request(m, true);
 
     pa_modargs_free(ma);
 
     pa_modargs_free(ma);
-    return 0;
+
+    return n ? 0 : -1;
 
 fail:
 
 fail:
-    pa__done(m);
     if (ma)
         pa_modargs_free(ma);
 
     if (ma)
         pa_modargs_free(ma);
 
-    return  -1;
+    return -1;
 }
 }
-
-static void free_func(void *p, void *userdata) {
-    struct rule *r = p;
-    assert(r);
-
-    pa_xfree(r->name);
-    pa_xfree(r->sink);
-    pa_xfree(r->source);
-    pa_xfree(r);
-}
-
-void pa__done(pa_module*m) {
-    struct userdata* u;
-
-    assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->subscription)
-        pa_subscription_free(u->subscription);
-
-    if (u->sink_input_hook_slot)
-        pa_hook_slot_free(u->sink_input_hook_slot);
-    if (u->source_output_hook_slot)
-        pa_hook_slot_free(u->source_output_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);
-}
-
-