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
42 PA_MODULE_AUTHOR("Michael Terry");
43 PA_MODULE_DESCRIPTION("When a sink/source is added, switch to it or conditionally switch to it");
44 PA_MODULE_VERSION(PACKAGE_VERSION);
45 PA_MODULE_LOAD_ONCE(true);
46 PA_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
52 static const char* const valid_modargs[] = {
53 "only_from_unavailable",
54 "ignore_virtual",
55 "blacklist",
56 NULL,
57 };
58
59 struct userdata {
60 bool only_from_unavailable;
61 bool ignore_virtual;
62 char *blacklist;
63 };
64
sink_put_hook_callback(pa_core * c,pa_sink * sink,void * userdata)65 static 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
source_put_hook_callback(pa_core * c,pa_source * source,void * userdata)124 static 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
pa__init(pa_module * m)184 int 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
228 fail:
229 if (ma)
230 pa_modargs_free(ma);
231
232 pa__done(m);
233
234 return -1;
235 }
236
pa__done(pa_module * m)237 void 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