• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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