1/***
2  This file is part of PulseAudio.
3
4  Copyright 2006-2008 Lennart Poettering
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License,
9  or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <errno.h>
25#include <stdio.h>
26
27#include <pulse/rtclock.h>
28#include <pulse/timeval.h>
29#include <pulse/xmalloc.h>
30
31#include <pulsecore/core-util.h>
32#include <pulsecore/module.h>
33#include <pulsecore/log.h>
34#include <pulsecore/namereg.h>
35#include <pulsecore/core-error.h>
36
37PA_MODULE_AUTHOR("Lennart Poettering");
38PA_MODULE_DESCRIPTION("Automatically restore the default sink and source");
39PA_MODULE_VERSION(PACKAGE_VERSION);
40PA_MODULE_LOAD_ONCE(true);
41
42#define SAVE_INTERVAL (5 * PA_USEC_PER_SEC)
43
44struct userdata {
45    pa_core *core;
46    pa_subscription *subscription;
47    pa_time_event *time_event;
48    char *sink_filename, *source_filename;
49    bool modified;
50};
51
52static void load(struct userdata *u) {
53    FILE *f;
54
55    /* We never overwrite manually configured settings */
56
57    if (u->core->configured_default_sink)
58        pa_log_info("Manually configured default sink, not overwriting.");
59    else if ((f = pa_fopen_cloexec(u->sink_filename, "r"))) {
60        char ln[256] = "";
61
62        if (fgets(ln, sizeof(ln)-1, f))
63            pa_strip_nl(ln);
64        fclose(f);
65
66        if (!ln[0])
67            pa_log_info("No previous default sink setting, ignoring.");
68        else if (!pa_namereg_is_valid_name(ln))
69            pa_log_warn("Invalid sink name: %s", ln);
70        else {
71            pa_log_info("Restoring default sink '%s'.", ln);
72            pa_core_set_configured_default_sink(u->core, ln);
73        }
74
75    } else if (errno != ENOENT)
76        pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
77
78    if (u->core->configured_default_source)
79        pa_log_info("Manually configured default source, not overwriting.");
80    else if ((f = pa_fopen_cloexec(u->source_filename, "r"))) {
81        char ln[256] = "";
82
83        if (fgets(ln, sizeof(ln)-1, f))
84            pa_strip_nl(ln);
85        fclose(f);
86
87        if (!ln[0])
88            pa_log_info("No previous default source setting, ignoring.");
89        else if (!pa_namereg_is_valid_name(ln))
90            pa_log_warn("Invalid source name: %s", ln);
91        else {
92            pa_log_info("Restoring default source '%s'.", ln);
93            pa_core_set_configured_default_source(u->core, ln);
94        }
95
96    } else if (errno != ENOENT)
97        pa_log("Failed to load default source: %s", pa_cstrerror(errno));
98}
99
100static void save(struct userdata *u) {
101    FILE *f;
102
103    if (!u->modified)
104        return;
105
106    if (u->sink_filename) {
107        if ((f = pa_fopen_cloexec(u->sink_filename, "w"))) {
108            fprintf(f, "%s\n", u->core->configured_default_sink ? u->core->configured_default_sink : "");
109            fclose(f);
110        } else
111            pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
112    }
113
114    if (u->source_filename) {
115        if ((f = pa_fopen_cloexec(u->source_filename, "w"))) {
116            fprintf(f, "%s\n", u->core->configured_default_source ? u->core->configured_default_source : "");
117            fclose(f);
118        } else
119            pa_log("Failed to save default source: %s", pa_cstrerror(errno));
120    }
121
122    u->modified = false;
123}
124
125static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
126    struct userdata *u = userdata;
127
128    pa_assert(u);
129    save(u);
130
131    if (u->time_event) {
132        u->core->mainloop->time_free(u->time_event);
133        u->time_event = NULL;
134    }
135}
136
137static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
138    struct userdata *u = userdata;
139
140    pa_assert(u);
141
142    u->modified = true;
143
144    if (!u->time_event)
145        u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, time_cb, u);
146}
147
148int pa__init(pa_module *m) {
149    struct userdata *u;
150
151    pa_assert(m);
152
153    m->userdata = u = pa_xnew0(struct userdata, 1);
154    u->core = m->core;
155
156    if (!(u->sink_filename = pa_state_path("default-sink", true)))
157        goto fail;
158
159    if (!(u->source_filename = pa_state_path("default-source", true)))
160        goto fail;
161
162    load(u);
163
164    u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
165
166    return 0;
167
168fail:
169    pa__done(m);
170
171    return -1;
172}
173
174void pa__done(pa_module*m) {
175    struct userdata *u;
176
177    pa_assert(m);
178
179    if (!(u = m->userdata))
180        return;
181
182    save(u);
183
184    if (u->subscription)
185        pa_subscription_free(u->subscription);
186
187    if (u->time_event)
188        m->core->mainloop->time_free(u->time_event);
189
190    pa_xfree(u->sink_filename);
191    pa_xfree(u->source_filename);
192    pa_xfree(u);
193}
194