2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2009 Canonical Ltd
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.
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.
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/>.
25 #include <pulse/xmalloc.h>
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>
36 #include "module-switch-on-connect-symdef.h"
38 PA_MODULE_AUTHOR("Michael Terry");
39 PA_MODULE_DESCRIPTION("When a sink/source is added, switch to it or conditionally switch to it");
40 PA_MODULE_VERSION(PACKAGE_VERSION);
41 PA_MODULE_LOAD_ONCE(true);
43 "only_from_unavailable=<boolean, only switch from unavailable ports> "
46 static const char* const valid_modargs[] = {
47 "only_from_unavailable",
52 bool only_from_unavailable;
55 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
58 pa_sink *old_default_sink;
60 struct userdata *u = userdata;
66 /* Don't want to run during startup or shutdown */
67 if (c->state != PA_CORE_RUNNING)
70 /* Don't switch to any internal devices */
71 if ((s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS))) {
72 if (pa_streq(s, "pci"))
74 else if (pa_streq(s, "isa"))
78 /* No default sink, nothing to move away, just set the new default */
79 if (!c->default_sink) {
80 pa_core_set_configured_default_sink(c, sink);
84 if (c->default_sink == sink)
87 if (u->only_from_unavailable)
88 if (!c->default_sink->active_port || c->default_sink->active_port->available != PA_AVAILABLE_NO)
91 old_default_sink = c->default_sink;
93 /* Actually do the switch to the new sink */
94 pa_core_set_configured_default_sink(c, sink);
96 /* Now move all old inputs over */
97 if (pa_idxset_size(old_default_sink->inputs) <= 0) {
98 pa_log_debug("No sink inputs to move away.");
102 PA_IDXSET_FOREACH(i, old_default_sink->inputs, idx) {
103 if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state))
106 if (pa_sink_input_move_to(i, sink, false) < 0)
107 pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
108 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
110 pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
111 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
117 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void* userdata) {
120 pa_source *old_default_source;
122 struct userdata *u = userdata;
128 /* Don't want to run during startup or shutdown */
129 if (c->state != PA_CORE_RUNNING)
132 /* Don't switch to a monitoring source */
133 if (source->monitor_of)
136 /* Don't switch to any internal devices */
137 if ((s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS))) {
138 if (pa_streq(s, "pci"))
140 else if (pa_streq(s, "isa"))
144 /* No default source, nothing to move away, just set the new default */
145 if (!c->default_source) {
146 pa_core_set_configured_default_source(c, source);
150 if (c->default_source == source)
153 if (u->only_from_unavailable)
154 if (!c->default_source->active_port || c->default_source->active_port->available != PA_AVAILABLE_NO)
157 old_default_source = c->default_source;
159 /* Actually do the switch to the new source */
160 pa_core_set_configured_default_source(c, source);
162 /* Now move all old outputs over */
163 if (pa_idxset_size(old_default_source->outputs) <= 0) {
164 pa_log_debug("No source outputs to move away.");
168 PA_IDXSET_FOREACH(o, old_default_source->outputs, idx) {
169 if (o->save_source || !PA_SOURCE_OUTPUT_IS_LINKED(o->state))
172 if (pa_source_output_move_to(o, source, false) < 0)
173 pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
174 pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), source->name);
176 pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index,
177 pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), source->name);
183 int pa__init(pa_module*m) {
189 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
190 pa_log("Failed to parse module arguments");
194 m->userdata = u = pa_xnew0(struct userdata, 1);
196 /* A little bit later than module-rescue-streams... */
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);
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.");
217 void pa__done(pa_module*m) {
222 if (!(u = m->userdata))