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