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
37 PA_MODULE_AUTHOR("Lennart Poettering");
38 PA_MODULE_DESCRIPTION("Automatically restore the default sink and source");
39 PA_MODULE_VERSION(PACKAGE_VERSION);
40 PA_MODULE_LOAD_ONCE(true);
41
42 #define SAVE_INTERVAL (5 * PA_USEC_PER_SEC)
43
44 struct 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
load(struct userdata * u)52 static 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
save(struct userdata * u)100 static 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
time_cb(pa_mainloop_api * a,pa_time_event * e,const struct timeval * t,void * userdata)125 static 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
subscribe_cb(pa_core * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)137 static 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
pa__init(pa_module * m)148 int 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
168 fail:
169 pa__done(m);
170
171 return -1;
172 }
173
pa__done(pa_module * m)174 void 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