]> code.delx.au - pulseaudio/blob - src/modules/module-switch-on-connect.c
alsa-mixer: Add surround 2.1 profile
[pulseaudio] / 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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pulse/xmalloc.h>
28
29 #include <pulsecore/core.h>
30 #include <pulsecore/sink-input.h>
31 #include <pulsecore/source-output.h>
32 #include <pulsecore/source.h>
33 #include <pulsecore/modargs.h>
34 #include <pulsecore/log.h>
35 #include <pulsecore/namereg.h>
36 #include <pulsecore/core-util.h>
37
38 #include "module-switch-on-connect-symdef.h"
39
40 PA_MODULE_AUTHOR("Michael Terry");
41 PA_MODULE_DESCRIPTION("When a sink/source is added, switch to it");
42 PA_MODULE_VERSION(PACKAGE_VERSION);
43 PA_MODULE_LOAD_ONCE(true);
44
45 static const char* const valid_modargs[] = {
46 NULL,
47 };
48
49 struct userdata {
50 pa_hook_slot
51 *sink_put_slot,
52 *source_put_slot;
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 *def;
59 const char *s;
60
61 pa_assert(c);
62 pa_assert(sink);
63
64 /* Don't want to run during startup or shutdown */
65 if (c->state != PA_CORE_RUNNING)
66 return PA_HOOK_OK;
67
68 /* Don't switch to any internal devices */
69 if ((s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS))) {
70 if (pa_streq(s, "pci"))
71 return PA_HOOK_OK;
72 else if (pa_streq(s, "isa"))
73 return PA_HOOK_OK;
74 }
75
76 def = pa_namereg_get_default_sink(c);
77 if (def == sink)
78 return PA_HOOK_OK;
79
80 /* Actually do the switch to the new sink */
81 pa_namereg_set_default_sink(c, sink);
82
83 /* Now move all old inputs over */
84 if (pa_idxset_size(def->inputs) <= 0) {
85 pa_log_debug("No sink inputs to move away.");
86 return PA_HOOK_OK;
87 }
88
89 PA_IDXSET_FOREACH(i, def->inputs, idx) {
90 if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state))
91 continue;
92
93 if (pa_sink_input_move_to(i, sink, false) < 0)
94 pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
95 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
96 else
97 pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
98 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), sink->name);
99 }
100
101 return PA_HOOK_OK;
102 }
103
104 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void* userdata) {
105 pa_source_output *o;
106 uint32_t idx;
107 pa_source *def;
108 const char *s;
109
110 pa_assert(c);
111 pa_assert(source);
112
113 /* Don't want to run during startup or shutdown */
114 if (c->state != PA_CORE_RUNNING)
115 return PA_HOOK_OK;
116
117 /* Don't switch to a monitoring source */
118 if (source->monitor_of)
119 return PA_HOOK_OK;
120
121 /* Don't switch to any internal devices */
122 if ((s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS))) {
123 if (pa_streq(s, "pci"))
124 return PA_HOOK_OK;
125 else if (pa_streq(s, "isa"))
126 return PA_HOOK_OK;
127 }
128
129 def = pa_namereg_get_default_source(c);
130 if (def == source)
131 return PA_HOOK_OK;
132
133 /* Actually do the switch to the new source */
134 pa_namereg_set_default_source(c, source);
135
136 /* Now move all old outputs over */
137 if (pa_idxset_size(def->outputs) <= 0) {
138 pa_log_debug("No source outputs to move away.");
139 return PA_HOOK_OK;
140 }
141
142 PA_IDXSET_FOREACH(o, def->outputs, idx) {
143 if (o->save_source || !PA_SOURCE_OUTPUT_IS_LINKED(o->state))
144 continue;
145
146 if (pa_source_output_move_to(o, source, false) < 0)
147 pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
148 pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), source->name);
149 else
150 pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index,
151 pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), source->name);
152 }
153
154 return PA_HOOK_OK;
155 }
156
157 int pa__init(pa_module*m) {
158 pa_modargs *ma;
159 struct userdata *u;
160
161 pa_assert(m);
162
163 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
164 pa_log("Failed to parse module arguments");
165 return -1;
166 }
167
168 m->userdata = u = pa_xnew(struct userdata, 1);
169
170 /* A little bit later than module-rescue-streams... */
171 u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+30, (pa_hook_cb_t) sink_put_hook_callback, u);
172 u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+20, (pa_hook_cb_t) source_put_hook_callback, u);
173
174 pa_modargs_free(ma);
175 return 0;
176 }
177
178 void pa__done(pa_module*m) {
179 struct userdata *u;
180
181 pa_assert(m);
182
183 if (!(u = m->userdata))
184 return;
185
186 if (u->sink_put_slot)
187 pa_hook_slot_free(u->sink_put_slot);
188 if (u->source_put_slot)
189 pa_hook_slot_free(u->source_put_slot);
190
191 pa_xfree(u);
192 }