1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2006 Lennart Poettering 5 Copyright 2009 Canonical Ltd 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published 9 by the Free Software Foundation; either version 2.1 of the License, 10 or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <pulse/xmalloc.h> 26 27#include <pulsecore/core.h> 28#include <pulsecore/sink-input.h> 29#include <pulsecore/source-output.h> 30#include <pulsecore/source.h> 31#include <pulsecore/modargs.h> 32#include <pulsecore/log.h> 33#include <pulsecore/namereg.h> 34#include <pulsecore/core-util.h> 35 36/* Ignore HDMI devices by default. HDMI monitors don't necessarily have audio 37 * output on them, and even if they do, waking up from sleep or changing 38 * monitor resolution may appear as a plugin event, which causes trouble if the 39 * user doesn't want to use the monitor for audio. */ 40#define DEFAULT_BLACKLIST "hdmi" 41 42PA_MODULE_AUTHOR("Michael Terry"); 43PA_MODULE_DESCRIPTION("When a sink/source is added, switch to it or conditionally switch to it"); 44PA_MODULE_VERSION(PACKAGE_VERSION); 45PA_MODULE_LOAD_ONCE(true); 46PA_MODULE_USAGE( 47 "only_from_unavailable=<boolean, only switch from unavailable ports> " 48 "ignore_virtual=<boolean, ignore new virtual sinks and sources, defaults to true> " 49 "blacklist=<regex, ignore matching devices> " 50); 51 52static const char* const valid_modargs[] = { 53 "only_from_unavailable", 54 "ignore_virtual", 55 "blacklist", 56 NULL, 57}; 58 59struct userdata { 60 bool only_from_unavailable; 61 bool ignore_virtual; 62 char *blacklist; 63}; 64 65static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { 66 const char *s; 67 struct userdata *u = userdata; 68 69 pa_assert(c); 70 pa_assert(sink); 71 pa_assert(userdata); 72 73 /* Don't want to run during startup or shutdown */ 74 if (c->state != PA_CORE_RUNNING) 75 return PA_HOOK_OK; 76 77 pa_log_debug("Trying to switch to new sink %s", sink->name); 78 79 /* Don't switch to any internal devices except HDMI */ 80 s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_STRING); 81 if (s && !pa_startswith(s, "hdmi")) { 82 s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS); 83 if (pa_safe_streq(s, "pci") || pa_safe_streq(s, "isa")) { 84 pa_log_debug("Refusing to switch to sink on %s bus", s); 85 return PA_HOOK_OK; 86 } 87 } 88 89 /* Ignore sinks matching the blacklist regex */ 90 if (u->blacklist && (pa_match(u->blacklist, sink->name) > 0)) { 91 pa_log_info("Refusing to switch to blacklisted sink %s", sink->name); 92 return PA_HOOK_OK; 93 } 94 95 /* Ignore virtual sinks if not configured otherwise on the command line */ 96 if (u->ignore_virtual && !(sink->flags & PA_SINK_HARDWARE)) { 97 pa_log_debug("Refusing to switch to virtual sink"); 98 return PA_HOOK_OK; 99 } 100 101 /* No default sink, nothing to move away, just set the new default */ 102 if (!c->default_sink) { 103 pa_core_set_configured_default_sink(c, sink->name); 104 return PA_HOOK_OK; 105 } 106 107 if (c->default_sink == sink) { 108 pa_log_debug("%s already is the default sink", sink->name); 109 return PA_HOOK_OK; 110 } 111 112 if (u->only_from_unavailable) 113 if (!c->default_sink->active_port || c->default_sink->active_port->available != PA_AVAILABLE_NO) { 114 pa_log_debug("Current default sink is available and module argument only_from_unavailable was set"); 115 return PA_HOOK_OK; 116 } 117 118 /* Actually do the switch to the new sink */ 119 pa_core_set_configured_default_sink(c, sink->name); 120 121 return PA_HOOK_OK; 122} 123 124static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void* userdata) { 125 const char *s; 126 struct userdata *u = userdata; 127 128 pa_assert(c); 129 pa_assert(source); 130 pa_assert(userdata); 131 132 /* Don't want to run during startup or shutdown */ 133 if (c->state != PA_CORE_RUNNING) 134 return PA_HOOK_OK; 135 136 /* Don't switch to a monitoring source */ 137 if (source->monitor_of) 138 return PA_HOOK_OK; 139 140 pa_log_debug("Trying to switch to new source %s", source->name); 141 142 /* Don't switch to any internal devices */ 143 s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS); 144 if (pa_safe_streq(s, "pci") || pa_safe_streq(s, "isa")) { 145 pa_log_debug("Refusing to switch to source on %s bus", s); 146 return PA_HOOK_OK; 147 } 148 149 /* Ignore sources matching the blacklist regex */ 150 if (u->blacklist && (pa_match(u->blacklist, source->name) > 0)) { 151 pa_log_info("Refusing to switch to blacklisted source %s", source->name); 152 return PA_HOOK_OK; 153 } 154 155 /* Ignore virtual sources if not configured otherwise on the command line */ 156 if (u->ignore_virtual && !(source->flags & PA_SOURCE_HARDWARE)) { 157 pa_log_debug("Refusing to switch to virtual source"); 158 return PA_HOOK_OK; 159 } 160 161 /* No default source, nothing to move away, just set the new default */ 162 if (!c->default_source) { 163 pa_core_set_configured_default_source(c, source->name); 164 return PA_HOOK_OK; 165 } 166 167 if (c->default_source == source) { 168 pa_log_debug("%s already is the default source", source->name); 169 return PA_HOOK_OK; 170 } 171 172 if (u->only_from_unavailable) 173 if (!c->default_source->active_port || c->default_source->active_port->available != PA_AVAILABLE_NO) { 174 pa_log_debug("Current default source is available and module argument only_from_unavailable was set"); 175 return PA_HOOK_OK; 176 } 177 178 /* Actually do the switch to the new source */ 179 pa_core_set_configured_default_source(c, source->name); 180 181 return PA_HOOK_OK; 182} 183 184int pa__init(pa_module*m) { 185 pa_modargs *ma; 186 struct userdata *u; 187 188 pa_assert(m); 189 190 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 191 pa_log("Failed to parse module arguments"); 192 return -1; 193 } 194 195 m->userdata = u = pa_xnew0(struct userdata, 1); 196 197 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+30, (pa_hook_cb_t) sink_put_hook_callback, u); 198 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+20, (pa_hook_cb_t) source_put_hook_callback, u); 199 200 if (pa_modargs_get_value_boolean(ma, "only_from_unavailable", &u->only_from_unavailable) < 0) { 201 pa_log("Failed to get a boolean value for only_from_unavailable."); 202 goto fail; 203 } 204 205 u->ignore_virtual = true; 206 if (pa_modargs_get_value_boolean(ma, "ignore_virtual", &u->ignore_virtual) < 0) { 207 pa_log("Failed to get a boolean value for ignore_virtual."); 208 goto fail; 209 } 210 211 u->blacklist = pa_xstrdup(pa_modargs_get_value(ma, "blacklist", DEFAULT_BLACKLIST)); 212 213 /* An empty string disables all blacklisting. */ 214 if (!*u->blacklist) { 215 pa_xfree(u->blacklist); 216 u->blacklist = NULL; 217 } 218 219 if (u->blacklist != NULL && !pa_is_regex_valid(u->blacklist)) { 220 pa_log_error("A blacklist pattern was provided but is not a valid regex"); 221 pa_xfree(u->blacklist); 222 goto fail; 223 } 224 225 pa_modargs_free(ma); 226 return 0; 227 228fail: 229 if (ma) 230 pa_modargs_free(ma); 231 232 pa__done(m); 233 234 return -1; 235} 236 237void pa__done(pa_module*m) { 238 struct userdata *u; 239 240 pa_assert(m); 241 242 if (!(u = m->userdata)) 243 return; 244 245 if (u->blacklist) 246 pa_xfree(u->blacklist); 247 248 pa_xfree(u); 249} 250