sink/source: Don't update default sink/source before calling PA_CORE_HOOK_{SINK,SOURC...
[platform/upstream/pulseaudio.git] / src / modules / module-switch-on-connect.c
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 #include "module-switch-on-connect-symdef.h"
37
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);
42 PA_MODULE_USAGE(
43         "only_from_unavailable=<boolean, only switch from unavailable ports> "
44 );
45
46 static const char* const valid_modargs[] = {
47     "only_from_unavailable",
48     NULL,
49 };
50
51 struct userdata {
52     bool only_from_unavailable;
53 };
54
55 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
56     pa_sink_input *i;
57     uint32_t idx;
58     pa_sink *old_default_sink;
59     const char *s;
60     struct userdata *u = userdata;
61
62     pa_assert(c);
63     pa_assert(sink);
64     pa_assert(userdata);
65
66     /* Don't want to run during startup or shutdown */
67     if (c->state != PA_CORE_RUNNING)
68         return PA_HOOK_OK;
69
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"))
73             return PA_HOOK_OK;
74         else if (pa_streq(s, "isa"))
75             return PA_HOOK_OK;
76     }
77
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);
81         return PA_HOOK_OK;
82     }
83
84     if (c->default_sink == sink)
85         return PA_HOOK_OK;
86
87     if (u->only_from_unavailable)
88         if (!c->default_sink->active_port || c->default_sink->active_port->available != PA_AVAILABLE_NO)
89             return PA_HOOK_OK;
90
91     old_default_sink = c->default_sink;
92
93     /* Actually do the switch to the new sink */
94     pa_core_set_configured_default_sink(c, sink);
95
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.");
99         return PA_HOOK_OK;
100     }
101
102     PA_IDXSET_FOREACH(i, old_default_sink->inputs, idx) {
103         if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state))
104             continue;
105
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);
109         else
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);
112     }
113
114     return PA_HOOK_OK;
115 }
116
117 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void* userdata) {
118     pa_source_output *o;
119     uint32_t idx;
120     pa_source *old_default_source;
121     const char *s;
122     struct userdata *u = userdata;
123
124     pa_assert(c);
125     pa_assert(source);
126     pa_assert(userdata);
127
128     /* Don't want to run during startup or shutdown */
129     if (c->state != PA_CORE_RUNNING)
130         return PA_HOOK_OK;
131
132     /* Don't switch to a monitoring source */
133     if (source->monitor_of)
134         return PA_HOOK_OK;
135
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"))
139             return PA_HOOK_OK;
140         else if (pa_streq(s, "isa"))
141             return PA_HOOK_OK;
142     }
143
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);
147         return PA_HOOK_OK;
148     }
149
150     if (c->default_source == source)
151         return PA_HOOK_OK;
152
153     if (u->only_from_unavailable)
154         if (!c->default_source->active_port || c->default_source->active_port->available != PA_AVAILABLE_NO)
155             return PA_HOOK_OK;
156
157     old_default_source = c->default_source;
158
159     /* Actually do the switch to the new source */
160     pa_core_set_configured_default_source(c, source);
161
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.");
165         return PA_HOOK_OK;
166     }
167
168     PA_IDXSET_FOREACH(o, old_default_source->outputs, idx) {
169         if (o->save_source || !PA_SOURCE_OUTPUT_IS_LINKED(o->state))
170             continue;
171
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);
175         else
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);
178     }
179
180     return PA_HOOK_OK;
181 }
182
183 int pa__init(pa_module*m) {
184     pa_modargs *ma;
185     struct userdata *u;
186
187     pa_assert(m);
188
189     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
190         pa_log("Failed to parse module arguments");
191         return -1;
192     }
193
194     m->userdata = u = pa_xnew0(struct userdata, 1);
195
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);
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     pa_modargs_free(ma);
206     return 0;
207
208 fail:
209     if (ma)
210         pa_modargs_free(ma);
211
212     pa__done(m);
213
214     return -1;
215 }
216
217 void pa__done(pa_module*m) {
218     struct userdata *u;
219
220     pa_assert(m);
221
222     if (!(u = m->userdata))
223         return;
224
225     pa_xfree(u);
226 }