]> code.delx.au - pulseaudio/blob - src/modules/module-intended-roles.c
qpaeq: Make it python3 and python2 compatible
[pulseaudio] / src / modules / module-intended-roles.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulse/xmalloc.h>
27
28 #include <pulsecore/module.h>
29 #include <pulsecore/core-util.h>
30 #include <pulsecore/modargs.h>
31 #include <pulsecore/log.h>
32 #include <pulsecore/sink-input.h>
33 #include <pulsecore/source-output.h>
34 #include <pulsecore/namereg.h>
35
36 #include "module-intended-roles-symdef.h"
37
38 PA_MODULE_AUTHOR("Lennart Poettering");
39 PA_MODULE_DESCRIPTION("Automatically set device of streams based of intended roles of devices");
40 PA_MODULE_VERSION(PACKAGE_VERSION);
41 PA_MODULE_LOAD_ONCE(TRUE);
42 PA_MODULE_USAGE(
43 "on_hotplug=<When new device becomes available, recheck streams?> "
44 "on_rescue=<When device becomes unavailable, recheck streams?>");
45
46 static const char* const valid_modargs[] = {
47 "on_hotplug",
48 "on_rescue",
49 NULL
50 };
51
52 struct userdata {
53 pa_core *core;
54 pa_module *module;
55
56 pa_hook_slot
57 *sink_input_new_hook_slot,
58 *source_output_new_hook_slot,
59 *sink_put_hook_slot,
60 *source_put_hook_slot,
61 *sink_unlink_hook_slot,
62 *source_unlink_hook_slot;
63
64 pa_bool_t on_hotplug:1;
65 pa_bool_t on_rescue:1;
66 };
67
68 static pa_bool_t role_match(pa_proplist *proplist, const char *role) {
69 const char *ir;
70 char *r;
71 const char *state = NULL;
72
73 if (!(ir = pa_proplist_gets(proplist, PA_PROP_DEVICE_INTENDED_ROLES)))
74 return FALSE;
75
76 while ((r = pa_split_spaces(ir, &state))) {
77
78 if (pa_streq(role, r)) {
79 pa_xfree(r);
80 return TRUE;
81 }
82
83 pa_xfree(r);
84 }
85
86 return FALSE;
87 }
88
89 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
90 const char *role;
91 pa_sink *s, *def;
92 uint32_t idx;
93
94 pa_assert(c);
95 pa_assert(new_data);
96 pa_assert(u);
97
98 if (!new_data->proplist) {
99 pa_log_debug("New stream lacks property data.");
100 return PA_HOOK_OK;
101 }
102
103 if (new_data->sink) {
104 pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
105 return PA_HOOK_OK;
106 }
107
108 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
109 pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
110 return PA_HOOK_OK;
111 }
112
113 /* Prefer the default sink over any other sink, just in case... */
114 if ((def = pa_namereg_get_default_sink(c)))
115 if (role_match(def->proplist, role) && pa_sink_input_new_data_set_sink(new_data, def, FALSE))
116 return PA_HOOK_OK;
117
118 /* @todo: favour the highest priority device, not the first one we find? */
119 PA_IDXSET_FOREACH(s, c->sinks, idx) {
120 if (s == def)
121 continue;
122
123 if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
124 continue;
125
126 if (role_match(s->proplist, role) && pa_sink_input_new_data_set_sink(new_data, s, FALSE))
127 return PA_HOOK_OK;
128 }
129
130 return PA_HOOK_OK;
131 }
132
133 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
134 const char *role;
135 pa_source *s, *def;
136 uint32_t idx;
137
138 pa_assert(c);
139 pa_assert(new_data);
140 pa_assert(u);
141
142 if (!new_data->proplist) {
143 pa_log_debug("New stream lacks property data.");
144 return PA_HOOK_OK;
145 }
146
147 if (new_data->source) {
148 pa_log_debug("Not setting device for stream %s, because already set.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
149 return PA_HOOK_OK;
150 }
151
152 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) {
153 pa_log_debug("Not setting device for stream %s, because it lacks role.", pa_strnull(pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_NAME)));
154 return PA_HOOK_OK;
155 }
156
157 /* Prefer the default source over any other source, just in case... */
158 if ((def = pa_namereg_get_default_source(c)))
159 if (role_match(def->proplist, role)) {
160 pa_source_output_new_data_set_source(new_data, def, FALSE);
161 return PA_HOOK_OK;
162 }
163
164 PA_IDXSET_FOREACH(s, c->sources, idx) {
165 if (s->monitor_of)
166 continue;
167
168 if (s == def)
169 continue;
170
171 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
172 continue;
173
174 /* @todo: favour the highest priority device, not the first one we find? */
175 if (role_match(s->proplist, role)) {
176 pa_source_output_new_data_set_source(new_data, s, FALSE);
177 return PA_HOOK_OK;
178 }
179 }
180
181 return PA_HOOK_OK;
182 }
183
184 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
185 pa_sink_input *si;
186 uint32_t idx;
187
188 pa_assert(c);
189 pa_assert(sink);
190 pa_assert(u);
191 pa_assert(u->on_hotplug);
192
193 PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
194 const char *role;
195
196 if (si->sink == sink)
197 continue;
198
199 if (si->save_sink)
200 continue;
201
202 /* Skip this if it is already in the process of being moved
203 * anyway */
204 if (!si->sink)
205 continue;
206
207 /* It might happen that a stream and a sink are set up at the
208 same time, in which case we want to make sure we don't
209 interfere with that */
210 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
211 continue;
212
213 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
214 continue;
215
216 if (role_match(si->sink->proplist, role))
217 continue;
218
219 if (!role_match(sink->proplist, role))
220 continue;
221
222 pa_sink_input_move_to(si, sink, FALSE);
223 }
224
225 return PA_HOOK_OK;
226 }
227
228 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
229 pa_source_output *so;
230 uint32_t idx;
231
232 pa_assert(c);
233 pa_assert(source);
234 pa_assert(u);
235 pa_assert(u->on_hotplug);
236
237 if (source->monitor_of)
238 return PA_HOOK_OK;
239
240 PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
241 const char *role;
242
243 if (so->source == source)
244 continue;
245
246 if (so->save_source)
247 continue;
248
249 if (so->direct_on_input)
250 continue;
251
252 /* Skip this if it is already in the process of being moved
253 * anyway */
254 if (!so->source)
255 continue;
256
257 /* It might happen that a stream and a source are set up at the
258 same time, in which case we want to make sure we don't
259 interfere with that */
260 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
261 continue;
262
263 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
264 continue;
265
266 if (role_match(so->source->proplist, role))
267 continue;
268
269 if (!role_match(source->proplist, role))
270 continue;
271
272 pa_source_output_move_to(so, source, FALSE);
273 }
274
275 return PA_HOOK_OK;
276 }
277
278 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
279 pa_sink_input *si;
280 uint32_t idx;
281 pa_sink *def;
282
283 pa_assert(c);
284 pa_assert(sink);
285 pa_assert(u);
286 pa_assert(u->on_rescue);
287
288 /* There's no point in doing anything if the core is shut down anyway */
289 if (c->state == PA_CORE_SHUTDOWN)
290 return PA_HOOK_OK;
291
292 /* If there not default sink, then there is no sink at all */
293 if (!(def = pa_namereg_get_default_sink(c)))
294 return PA_HOOK_OK;
295
296 PA_IDXSET_FOREACH(si, sink->inputs, idx) {
297 const char *role;
298 uint32_t jdx;
299 pa_sink *d;
300
301 if (!si->sink)
302 continue;
303
304 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
305 continue;
306
307 /* Would the default sink fit? If so, let's use it */
308 if (def != sink && role_match(def->proplist, role))
309 if (pa_sink_input_move_to(si, def, FALSE) >= 0)
310 continue;
311
312 /* Try to find some other fitting sink */
313 /* @todo: favour the highest priority device, not the first one we find? */
314 PA_IDXSET_FOREACH(d, c->sinks, jdx) {
315 if (d == def || d == sink)
316 continue;
317
318 if (!PA_SINK_IS_LINKED(pa_sink_get_state(d)))
319 continue;
320
321 if (role_match(d->proplist, role))
322 if (pa_sink_input_move_to(si, d, FALSE) >= 0)
323 break;
324 }
325 }
326
327 return PA_HOOK_OK;
328 }
329
330 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
331 pa_source_output *so;
332 uint32_t idx;
333 pa_source *def;
334
335 pa_assert(c);
336 pa_assert(source);
337 pa_assert(u);
338 pa_assert(u->on_rescue);
339
340 /* There's no point in doing anything if the core is shut down anyway */
341 if (c->state == PA_CORE_SHUTDOWN)
342 return PA_HOOK_OK;
343
344 /* If there not default source, then there is no source at all */
345 if (!(def = pa_namereg_get_default_source(c)))
346 return PA_HOOK_OK;
347
348 PA_IDXSET_FOREACH(so, source->outputs, idx) {
349 const char *role;
350 uint32_t jdx;
351 pa_source *d;
352
353 if (so->direct_on_input)
354 continue;
355
356 if (!so->source)
357 continue;
358
359 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
360 continue;
361
362 /* Would the default source fit? If so, let's use it */
363 if (def != source && role_match(def->proplist, role) && !source->monitor_of == !def->monitor_of) {
364 pa_source_output_move_to(so, def, FALSE);
365 continue;
366 }
367
368 /* Try to find some other fitting source */
369 /* @todo: favour the highest priority device, not the first one we find? */
370 PA_IDXSET_FOREACH(d, c->sources, jdx) {
371 if (d == def || d == source)
372 continue;
373
374 if (!PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
375 continue;
376
377 /* If moving from a monitor, move to another monitor */
378 if (!source->monitor_of == !d->monitor_of && role_match(d->proplist, role)) {
379 pa_source_output_move_to(so, d, FALSE);
380 break;
381 }
382 }
383 }
384
385 return PA_HOOK_OK;
386 }
387
388 int pa__init(pa_module*m) {
389 pa_modargs *ma = NULL;
390 struct userdata *u;
391 pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
392
393 pa_assert(m);
394
395 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
396 pa_log("Failed to parse module arguments");
397 goto fail;
398 }
399
400 if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
401 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
402 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
403 goto fail;
404 }
405
406 m->userdata = u = pa_xnew0(struct userdata, 1);
407 u->core = m->core;
408 u->module = m;
409 u->on_hotplug = on_hotplug;
410 u->on_rescue = on_rescue;
411
412 /* A little bit later than module-stream-restore */
413 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) sink_input_new_hook_callback, u);
414 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+10, (pa_hook_cb_t) source_output_new_hook_callback, u);
415
416 if (on_hotplug) {
417 /* A little bit later than module-stream-restore */
418 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_put_hook_callback, u);
419 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+10, (pa_hook_cb_t) source_put_hook_callback, u);
420 }
421
422 if (on_rescue) {
423 /* A little bit later than module-stream-restore, a little bit earlier than module-rescue-streams, ... */
424 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) sink_unlink_hook_callback, u);
425 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+10, (pa_hook_cb_t) source_unlink_hook_callback, u);
426 }
427
428 pa_modargs_free(ma);
429 return 0;
430
431 fail:
432 pa__done(m);
433
434 if (ma)
435 pa_modargs_free(ma);
436
437 return -1;
438 }
439
440 void pa__done(pa_module*m) {
441 struct userdata* u;
442
443 pa_assert(m);
444
445 if (!(u = m->userdata))
446 return;
447
448 if (u->sink_input_new_hook_slot)
449 pa_hook_slot_free(u->sink_input_new_hook_slot);
450 if (u->source_output_new_hook_slot)
451 pa_hook_slot_free(u->source_output_new_hook_slot);
452
453 if (u->sink_put_hook_slot)
454 pa_hook_slot_free(u->sink_put_hook_slot);
455 if (u->source_put_hook_slot)
456 pa_hook_slot_free(u->source_put_hook_slot);
457
458 if (u->sink_unlink_hook_slot)
459 pa_hook_slot_free(u->sink_unlink_hook_slot);
460 if (u->source_unlink_hook_slot)
461 pa_hook_slot_free(u->source_unlink_hook_slot);
462
463 pa_xfree(u);
464 }