clean pulseaudio recipes
[scm/bb/meta-tizen.git] / recipes-multimedia / pulseaudio / pulseaudio_5.0 / 0082-Add-module-main-volume-policy.patch
1 From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
2 Date: Wed, 21 May 2014 14:13:41 +0300
3 Subject: Add module-main-volume-policy
4
5 Change-Id: I787141b43cafb652aa752c64ae28b6b7aa052d8e
6 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
7 ---
8  Makefile.am                                        |   3 +
9  src/Makefile.am                                    |  15 +
10  src/daemon/default.pa.in                           |   4 +
11  .../main-volume-policy/main-volume-context.c       | 325 ++++++++++++
12  .../main-volume-policy/main-volume-context.h       |  75 +++
13  .../main-volume-policy/main-volume-policy.c        | 213 ++++++++
14  .../main-volume-policy.conf.example                |  20 +
15  .../main-volume-policy/main-volume-policy.h        |  72 +++
16  .../main-volume-policy/module-main-volume-policy.c | 556 +++++++++++++++++++++
17  9 files changed, 1283 insertions(+)
18  create mode 100644 src/modules/main-volume-policy/main-volume-context.c
19  create mode 100644 src/modules/main-volume-policy/main-volume-context.h
20  create mode 100644 src/modules/main-volume-policy/main-volume-policy.c
21  create mode 100644 src/modules/main-volume-policy/main-volume-policy.conf.example
22  create mode 100644 src/modules/main-volume-policy/main-volume-policy.h
23  create mode 100644 src/modules/main-volume-policy/module-main-volume-policy.c
24
25 diff --git a/Makefile.am b/Makefile.am
26 index cf4a648..646b7fc 100644
27 --- a/Makefile.am
28 +++ b/Makefile.am
29 @@ -61,6 +61,9 @@ moduledevinternaldir   = $(includedir)/pulsemodule/pulse
30  moduledevvolumeapi_DATA = src/modules/volume-api/*.h
31  moduledevvolumeapidir   = $(includedir)/pulsemodule/modules/volume-api
32  
33 +moduledevmainvolumepolicy_DATA = src/modules/main-volume-policy/*.h
34 +moduledevmainvolumepolicydir   = $(includedir)/pulsemodule/modules/main-volume-policy
35 +
36  filterdir = /etc/pulse/filter
37  filter_DATA = filter/filter_44100_48000.dat \
38               filter/filter_44100_8000.dat \
39 diff --git a/src/Makefile.am b/src/Makefile.am
40 index a6bb319..8fa60ec 100644
41 --- a/src/Makefile.am
42 +++ b/src/Makefile.am
43 @@ -1017,6 +1017,7 @@ libpulsecore_foreign_la_CFLAGS = $(AM_CFLAGS) $(FOREIGN_CFLAGS)
44  
45  modlibexec_LTLIBRARIES = \
46                 libcli.la \
47 +               libmain-volume-policy.la \
48                 libprotocol-cli.la \
49                 libprotocol-simple.la \
50                 libprotocol-http.la \
51 @@ -1051,6 +1052,12 @@ libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h
52  libcli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
53  libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
54  
55 +libmain_volume_policy_la_SOURCES = \
56 +               modules/main-volume-policy/main-volume-context.c modules/main-volume-policy/main-volume-context.h \
57 +               modules/main-volume-policy/main-volume-policy.c modules/main-volume-policy/main-volume-policy.h
58 +libmain_volume_policy_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
59 +libmain_volume_policy_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libvolume-api.la
60 +
61  libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
62  libprotocol_cli_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
63  libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libcli.la
64 @@ -1133,6 +1140,7 @@ endif
65  modlibexec_LTLIBRARIES += \
66                 module-cli.la \
67                 module-cli-protocol-tcp.la \
68 +               module-main-volume-policy.la \
69                 module-simple-protocol-tcp.la \
70                 module-volume-api.la \
71                 module-null-sink.la \
72 @@ -1426,6 +1434,7 @@ SYMDEF_FILES = \
73                 module-cli-symdef.h \
74                 module-cli-protocol-tcp-symdef.h \
75                 module-cli-protocol-unix-symdef.h \
76 +               module-main-volume-policy-symdef.h \
77                 module-pipe-sink-symdef.h \
78                 module-pipe-source-symdef.h \
79                 module-simple-protocol-tcp-symdef.h \
80 @@ -1575,6 +1584,12 @@ module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_
81  module_cli_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
82  module_cli_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-cli.la
83  
84 +# Main volume and mute policy
85 +
86 +module_main_volume_policy_la_SOURCES = modules/main-volume-policy/module-main-volume-policy.c
87 +module_main_volume_policy_la_LDFLAGS = $(MODULE_LDFLAGS)
88 +module_main_volume_policy_la_LIBADD = $(MODULE_LIBADD) libmain-volume-policy.la libvolume-api.la
89 +
90  # HTTP protocol
91  
92  module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
93 diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
94 index 7cf52a4..f70804c 100755
95 --- a/src/daemon/default.pa.in
96 +++ b/src/daemon/default.pa.in
97 @@ -197,6 +197,10 @@ load-module module-volume-api
98  load-module module-audio-groups
99  .endif
100  
101 +.ifexists module-main-volume-policy
102 +load-module module-main-volume-policy
103 +.endif
104 +
105  ### Make some devices default
106  #set-default-sink output
107  #set-default-source input
108 diff --git a/src/modules/main-volume-policy/main-volume-context.c b/src/modules/main-volume-policy/main-volume-context.c
109 new file mode 100644
110 index 0000000..7ac35c6
111 --- /dev/null
112 +++ b/src/modules/main-volume-policy/main-volume-context.c
113 @@ -0,0 +1,325 @@
114 +/***
115 +  This file is part of PulseAudio.
116 +
117 +  Copyright 2014 Intel Corporation
118 +
119 +  PulseAudio is free software; you can redistribute it and/or modify
120 +  it under the terms of the GNU Lesser General Public License as published
121 +  by the Free Software Foundation; either version 2.1 of the License,
122 +  or (at your option) any later version.
123 +
124 +  PulseAudio is distributed in the hope that it will be useful, but
125 +  WITHOUT ANY WARRANTY; without even the implied warranty of
126 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
127 +  General Public License for more details.
128 +
129 +  You should have received a copy of the GNU Lesser General Public License
130 +  along with PulseAudio; if not, write to the Free Software
131 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
132 +  USA.
133 +***/
134 +
135 +#ifdef HAVE_CONFIG_H
136 +#include <config.h>
137 +#endif
138 +
139 +#include "main-volume-context.h"
140 +
141 +#include <modules/volume-api/mute-control.h>
142 +#include <modules/volume-api/volume-control.h>
143 +
144 +int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
145 +                               pa_main_volume_context **context) {
146 +    pa_main_volume_context *context_local;
147 +    int r;
148 +
149 +    pa_assert(policy);
150 +    pa_assert(name);
151 +    pa_assert(description);
152 +    pa_assert(context);
153 +
154 +    context_local = pa_xnew0(struct pa_main_volume_context, 1);
155 +    context_local->main_volume_policy = policy;
156 +    context_local->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
157 +
158 +    r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name);
159 +    if (r < 0)
160 +        goto fail;
161 +
162 +    context_local->description = pa_xstrdup(description);
163 +
164 +    *context = context_local;
165 +
166 +    return 0;
167 +
168 +fail:
169 +    pa_main_volume_context_free(context_local);
170 +
171 +    return r;
172 +}
173 +
174 +void pa_main_volume_context_put(pa_main_volume_context *context) {
175 +    pa_assert(context);
176 +
177 +    pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context);
178 +
179 +    context->linked = true;
180 +
181 +    pa_log_debug("Created main volume context #%u.", context->index);
182 +    pa_log_debug("    Name: %s", context->name);
183 +    pa_log_debug("    Description: %s", context->description);
184 +    pa_log_debug("    Main output volume control: %s",
185 +                 context->main_output_volume_control ? context->main_output_volume_control->name : "(unset)");
186 +    pa_log_debug("    Main input volume control: %s",
187 +                 context->main_input_volume_control ? context->main_input_volume_control->name : "(unset)");
188 +    pa_log_debug("    Main output mute control: %s",
189 +                 context->main_output_mute_control ? context->main_output_mute_control->name : "(unset)");
190 +    pa_log_debug("    Main input mute control: %s",
191 +                 context->main_input_mute_control ? context->main_input_mute_control->name : "(unset)");
192 +
193 +    pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT], context);
194 +}
195 +
196 +void pa_main_volume_context_unlink(pa_main_volume_context *context) {
197 +    pa_assert(context);
198 +
199 +    if (context->unlinked) {
200 +        pa_log_debug("Unlinking main volume context %s (already unlinked, this is a no-op).", context->name);
201 +        return;
202 +    }
203 +
204 +    context->unlinked = true;
205 +
206 +    pa_log_debug("Unlinking main volume context %s.", context->name);
207 +
208 +    if (context->linked)
209 +        pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
210 +
211 +    if (context->main_input_mute_control_binding) {
212 +        pa_binding_free(context->main_input_mute_control_binding);
213 +        context->main_input_mute_control_binding = NULL;
214 +    }
215 +
216 +    if (context->main_output_mute_control_binding) {
217 +        pa_binding_free(context->main_output_mute_control_binding);
218 +        context->main_output_mute_control_binding = NULL;
219 +    }
220 +
221 +    if (context->main_input_volume_control_binding) {
222 +        pa_binding_free(context->main_input_volume_control_binding);
223 +        context->main_input_volume_control_binding = NULL;
224 +    }
225 +
226 +    if (context->main_output_volume_control_binding) {
227 +        pa_binding_free(context->main_output_volume_control_binding);
228 +        context->main_output_volume_control_binding = NULL;
229 +    }
230 +
231 +    context->main_input_mute_control = NULL;
232 +    context->main_output_mute_control = NULL;
233 +    context->main_input_volume_control = NULL;
234 +    context->main_output_volume_control = NULL;
235 +
236 +    pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
237 +}
238 +
239 +void pa_main_volume_context_free(pa_main_volume_context *context) {
240 +    pa_assert(context);
241 +
242 +    if (!context->unlinked)
243 +        pa_main_volume_context_unlink(context);
244 +
245 +    pa_xfree(context->description);
246 +
247 +    if (context->name)
248 +        pa_main_volume_policy_unregister_name(context->main_volume_policy, context->name);
249 +
250 +    pa_xfree(context);
251 +}
252 +
253 +const char *pa_main_volume_context_get_name(pa_main_volume_context *context) {
254 +    pa_assert(context);
255 +
256 +    return context->name;
257 +}
258 +
259 +static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
260 +    pa_volume_control *old_control;
261 +
262 +    pa_assert(context);
263 +
264 +    old_control = context->main_output_volume_control;
265 +
266 +    if (control == old_control)
267 +        return;
268 +
269 +    context->main_output_volume_control = control;
270 +
271 +    if (!context->linked || context->unlinked)
272 +        return;
273 +
274 +    pa_log_debug("The main output volume control of main volume context %s changed from %s to %s.", context->name,
275 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
276 +
277 +    pa_hook_fire(&context->main_volume_policy->hooks
278 +                     [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED],
279 +                 context);
280 +}
281 +
282 +void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
283 +                                                            pa_binding_target_info *target_info) {
284 +    pa_binding_owner_info owner_info = {
285 +        .userdata = context,
286 +        .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
287 +    };
288 +
289 +    pa_assert(context);
290 +    pa_assert(target_info);
291 +
292 +    if (context->main_output_volume_control_binding)
293 +        pa_binding_free(context->main_output_volume_control_binding);
294 +
295 +    context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
296 +                                                                 target_info);
297 +}
298 +
299 +static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
300 +    pa_volume_control *old_control;
301 +
302 +    pa_assert(context);
303 +
304 +    old_control = context->main_input_volume_control;
305 +
306 +    if (control == old_control)
307 +        return;
308 +
309 +    context->main_input_volume_control = control;
310 +
311 +    if (!context->linked || context->unlinked)
312 +        return;
313 +
314 +    pa_log_debug("The main input volume control of main volume context %s changed from %s to %s.", context->name,
315 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
316 +
317 +    pa_hook_fire(&context->main_volume_policy->hooks
318 +                     [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED],
319 +                 context);
320 +}
321 +
322 +void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
323 +                                                           pa_binding_target_info *target_info) {
324 +    pa_binding_owner_info owner_info = {
325 +        .userdata = context,
326 +        .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
327 +    };
328 +
329 +    pa_assert(context);
330 +    pa_assert(target_info);
331 +
332 +    if (context->main_input_volume_control_binding)
333 +        pa_binding_free(context->main_input_volume_control_binding);
334 +
335 +    context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
336 +                                                                target_info);
337 +}
338 +
339 +static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
340 +    pa_mute_control *old_control;
341 +
342 +    pa_assert(context);
343 +
344 +    old_control = context->main_output_mute_control;
345 +
346 +    if (control == old_control)
347 +        return;
348 +
349 +    context->main_output_mute_control = control;
350 +
351 +    if (!context->linked || context->unlinked)
352 +        return;
353 +
354 +    pa_log_debug("The main output mute control of main volume context %s changed from %s to %s.", context->name,
355 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
356 +
357 +    pa_hook_fire(&context->main_volume_policy->hooks
358 +                     [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED],
359 +                 context);
360 +}
361 +
362 +void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
363 +                                                          pa_binding_target_info *target_info) {
364 +    pa_binding_owner_info owner_info = {
365 +        .userdata = context,
366 +        .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
367 +    };
368 +
369 +    pa_assert(context);
370 +    pa_assert(target_info);
371 +
372 +    if (context->main_output_mute_control_binding)
373 +        pa_binding_free(context->main_output_mute_control_binding);
374 +
375 +    context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
376 +                                                               target_info);
377 +}
378 +
379 +static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
380 +    pa_mute_control *old_control;
381 +
382 +    pa_assert(context);
383 +
384 +    old_control = context->main_input_mute_control;
385 +
386 +    if (control == old_control)
387 +        return;
388 +
389 +    context->main_input_mute_control = control;
390 +
391 +    if (!context->linked || context->unlinked)
392 +        return;
393 +
394 +    pa_log_debug("The main input mute control of main volume context %s changed from %s to %s.", context->name,
395 +                 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
396 +
397 +    pa_hook_fire(&context->main_volume_policy->hooks
398 +                     [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
399 +                 context);
400 +}
401 +
402 +void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context,
403 +                                                         pa_binding_target_info *target_info) {
404 +    pa_binding_owner_info owner_info = {
405 +        .userdata = context,
406 +        .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
407 +    };
408 +
409 +    pa_assert(context);
410 +    pa_assert(target_info);
411 +
412 +    if (context->main_input_mute_control_binding)
413 +        pa_binding_free(context->main_input_mute_control_binding);
414 +
415 +    context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
416 +                                                              target_info);
417 +}
418 +
419 +pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) {
420 +    pa_binding_target_type *type;
421 +
422 +    pa_assert(policy);
423 +
424 +    type = pa_binding_target_type_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, policy->main_volume_contexts,
425 +                                      &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT],
426 +                                      &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK],
427 +                                      (pa_binding_target_type_get_name_cb_t) pa_main_volume_context_get_name);
428 +    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL,
429 +                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_volume_control));
430 +    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL,
431 +                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_volume_control));
432 +    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL,
433 +                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_mute_control));
434 +    pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL,
435 +                                     PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_mute_control));
436 +
437 +    return type;
438 +}
439 diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h
440 new file mode 100644
441 index 0000000..4a0a6f7
442 --- /dev/null
443 +++ b/src/modules/main-volume-policy/main-volume-context.h
444 @@ -0,0 +1,75 @@
445 +#ifndef foomainvolumecontexthfoo
446 +#define foomainvolumecontexthfoo
447 +
448 +/***
449 +  This file is part of PulseAudio.
450 +
451 +  Copyright 2014 Intel Corporation
452 +
453 +  PulseAudio is free software; you can redistribute it and/or modify
454 +  it under the terms of the GNU Lesser General Public License as published
455 +  by the Free Software Foundation; either version 2.1 of the License,
456 +  or (at your option) any later version.
457 +
458 +  PulseAudio is distributed in the hope that it will be useful, but
459 +  WITHOUT ANY WARRANTY; without even the implied warranty of
460 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
461 +  General Public License for more details.
462 +
463 +  You should have received a copy of the GNU Lesser General Public License
464 +  along with PulseAudio; if not, write to the Free Software
465 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
466 +  USA.
467 +***/
468 +
469 +#include <modules/main-volume-policy/main-volume-policy.h>
470 +
471 +#include <modules/volume-api/binding.h>
472 +
473 +typedef struct pa_main_volume_context pa_main_volume_context;
474 +
475 +#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE "MainVolumeContext"
476 +#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL "main_output_volume_control"
477 +#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL "main_input_volume_control"
478 +#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL "main_output_mute_control"
479 +#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL "main_input_mute_control"
480 +
481 +struct pa_main_volume_context {
482 +    pa_main_volume_policy *main_volume_policy;
483 +    uint32_t index;
484 +    const char *name;
485 +    char *description;
486 +    pa_volume_control *main_output_volume_control;
487 +    pa_volume_control *main_input_volume_control;
488 +    pa_mute_control *main_output_mute_control;
489 +    pa_mute_control *main_input_mute_control;
490 +
491 +    pa_binding *main_output_volume_control_binding;
492 +    pa_binding *main_input_volume_control_binding;
493 +    pa_binding *main_output_mute_control_binding;
494 +    pa_binding *main_input_mute_control_binding;
495 +
496 +    bool linked;
497 +    bool unlinked;
498 +};
499 +
500 +int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
501 +                               pa_main_volume_context **context);
502 +void pa_main_volume_context_put(pa_main_volume_context *context);
503 +void pa_main_volume_context_unlink(pa_main_volume_context *context);
504 +void pa_main_volume_context_free(pa_main_volume_context *context);
505 +
506 +const char *pa_main_volume_context_get_name(pa_main_volume_context *context);
507 +
508 +void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
509 +                                                            pa_binding_target_info *target_info);
510 +void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
511 +                                                           pa_binding_target_info *target_info);
512 +void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
513 +                                                          pa_binding_target_info *target_info);
514 +void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, pa_binding_target_info *target_info);
515 +
516 +/* Called from main-volume-policy.c only. */
517 +pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy);
518 +
519 +#endif
520 diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c
521 new file mode 100644
522 index 0000000..b0b4ede
523 --- /dev/null
524 +++ b/src/modules/main-volume-policy/main-volume-policy.c
525 @@ -0,0 +1,213 @@
526 +/***
527 +  This file is part of PulseAudio.
528 +
529 +  Copyright 2014 Intel Corporation
530 +
531 +  PulseAudio is free software; you can redistribute it and/or modify
532 +  it under the terms of the GNU Lesser General Public License as published
533 +  by the Free Software Foundation; either version 2.1 of the License,
534 +  or (at your option) any later version.
535 +
536 +  PulseAudio is distributed in the hope that it will be useful, but
537 +  WITHOUT ANY WARRANTY; without even the implied warranty of
538 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
539 +  General Public License for more details.
540 +
541 +  You should have received a copy of the GNU Lesser General Public License
542 +  along with PulseAudio; if not, write to the Free Software
543 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
544 +  USA.
545 +***/
546 +
547 +#ifdef HAVE_CONFIG_H
548 +#include <config.h>
549 +#endif
550 +
551 +#include "main-volume-policy.h"
552 +
553 +#include <modules/main-volume-policy/main-volume-context.h>
554 +
555 +#include <pulsecore/core-util.h>
556 +#include <pulsecore/shared.h>
557 +
558 +static pa_main_volume_policy *main_volume_policy_new(pa_core *core);
559 +static void main_volume_policy_free(pa_main_volume_policy *policy);
560 +
561 +pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core) {
562 +    pa_main_volume_policy *policy;
563 +
564 +    pa_assert(core);
565 +
566 +    policy = pa_shared_get(core, "main-volume-policy");
567 +
568 +    if (policy)
569 +        pa_main_volume_policy_ref(policy);
570 +    else {
571 +        policy = main_volume_policy_new(core);
572 +        pa_assert_se(pa_shared_set(core, "main-volume-policy", policy) >= 0);
573 +    }
574 +
575 +    return policy;
576 +}
577 +
578 +pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy) {
579 +    pa_assert(policy);
580 +
581 +    policy->refcnt++;
582 +
583 +    return policy;
584 +}
585 +
586 +void pa_main_volume_policy_unref(pa_main_volume_policy *policy) {
587 +    pa_assert(policy);
588 +    pa_assert(policy->refcnt > 0);
589 +
590 +    policy->refcnt--;
591 +
592 +    if (policy->refcnt == 0) {
593 +        pa_assert_se(pa_shared_remove(policy->core, "main-volume-policy") >= 0);
594 +        main_volume_policy_free(policy);
595 +    }
596 +}
597 +
598 +static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
599 +    pa_main_volume_policy *policy;
600 +    unsigned i;
601 +
602 +    pa_assert(core);
603 +
604 +    policy = pa_xnew0(pa_main_volume_policy, 1);
605 +    policy->core = core;
606 +    policy->refcnt = 1;
607 +    policy->volume_api = pa_volume_api_get(core);
608 +    policy->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
609 +    policy->main_volume_contexts = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
610 +
611 +    for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
612 +        pa_hook_init(&policy->hooks[i], policy);
613 +
614 +    policy->main_volume_context_binding_target_type = pa_main_volume_context_create_binding_target_type(policy);
615 +    pa_volume_api_add_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
616 +
617 +    pa_log_debug("Created a pa_main_volume_policy object.");
618 +
619 +    return policy;
620 +}
621 +
622 +static void main_volume_policy_free(pa_main_volume_policy *policy) {
623 +    unsigned i;
624 +
625 +    pa_assert(policy);
626 +    pa_assert(policy->refcnt == 0);
627 +
628 +    pa_log_debug("Freeing the pa_main_volume_policy object.");
629 +
630 +    if (policy->main_volume_context_binding_target_type) {
631 +        pa_volume_api_remove_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
632 +        pa_binding_target_type_free(policy->main_volume_context_binding_target_type);
633 +    }
634 +
635 +    for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
636 +        pa_hook_done(&policy->hooks[i]);
637 +
638 +    if (policy->main_volume_contexts) {
639 +        pa_assert(pa_hashmap_isempty(policy->main_volume_contexts));
640 +        pa_hashmap_free(policy->main_volume_contexts);
641 +    }
642 +
643 +    if (policy->names) {
644 +        pa_assert(pa_hashmap_isempty(policy->names));
645 +        pa_hashmap_free(policy->names);
646 +    }
647 +
648 +    if (policy->volume_api)
649 +        pa_volume_api_unref(policy->volume_api);
650 +
651 +    pa_xfree(policy);
652 +}
653 +
654 +int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const char *requested_name,
655 +                                        bool fail_if_already_registered, const char **registered_name) {
656 +    char *n;
657 +
658 +    pa_assert(policy);
659 +    pa_assert(requested_name);
660 +    pa_assert(registered_name);
661 +
662 +    n = pa_xstrdup(requested_name);
663 +
664 +    if (pa_hashmap_put(policy->names, n, n) < 0) {
665 +        unsigned i = 1;
666 +
667 +        pa_xfree(n);
668 +
669 +        if (fail_if_already_registered) {
670 +            pa_log("Name %s already registered.", requested_name);
671 +            return -PA_ERR_EXIST;
672 +        }
673 +
674 +        do {
675 +            i++;
676 +            n = pa_sprintf_malloc("%s.%u", requested_name, i);
677 +        } while (pa_hashmap_put(policy->names, n, n) < 0);
678 +    }
679 +
680 +    *registered_name = n;
681 +
682 +    return 0;
683 +}
684 +
685 +void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name) {
686 +    pa_assert(policy);
687 +    pa_assert(name);
688 +
689 +    pa_assert_se(pa_hashmap_remove_and_free(policy->names, name) >= 0);
690 +}
691 +
692 +uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy) {
693 +    uint32_t idx;
694 +
695 +    pa_assert(policy);
696 +
697 +    idx = policy->next_main_volume_context_index++;
698 +
699 +    return idx;
700 +}
701 +
702 +void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
703 +    pa_assert(policy);
704 +    pa_assert(context);
705 +
706 +    pa_assert_se(pa_hashmap_put(policy->main_volume_contexts, (void *) context->name, context) >= 0);
707 +}
708 +
709 +int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
710 +    pa_assert(policy);
711 +    pa_assert(context);
712 +
713 +    if (!pa_hashmap_remove(policy->main_volume_contexts, context->name))
714 +        return -1;
715 +
716 +    if (context == policy->active_main_volume_context)
717 +        pa_main_volume_policy_set_active_main_volume_context(policy, NULL);
718 +
719 +    return 0;
720 +}
721 +
722 +void pa_main_volume_policy_set_active_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
723 +    pa_main_volume_context *old_context;
724 +
725 +    pa_assert(policy);
726 +
727 +    old_context = policy->active_main_volume_context;
728 +
729 +    if (context == old_context)
730 +        return;
731 +
732 +    policy->active_main_volume_context = context;
733 +
734 +    pa_log_debug("The active main volume context changed from %s to %s.", old_context ? old_context->name : "(unset)",
735 +                 context ? context->name : "(unset)");
736 +
737 +    pa_hook_fire(&policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED], NULL);
738 +}
739 diff --git a/src/modules/main-volume-policy/main-volume-policy.conf.example b/src/modules/main-volume-policy/main-volume-policy.conf.example
740 new file mode 100644
741 index 0000000..a4a35d3
742 --- /dev/null
743 +++ b/src/modules/main-volume-policy/main-volume-policy.conf.example
744 @@ -0,0 +1,20 @@
745 +[General]
746 +output-volume-model = by-active-main-volume-context
747 +input-volume-model = by-active-main-volume-context
748 +output-mute-model = none
749 +input-mute-model = none
750 +main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
751 +
752 +[MainVolumeContext x-example-call-main-volume-context]
753 +description = Call main volume context
754 +main-output-volume-control = bind:AudioGroup:x-example-call-downlink-audio-group
755 +main-input-volume-control = bind:AudioGroup:x-example-call-uplink-audio-group
756 +main-output-mute-control = none
757 +main-input-mute-control = none
758 +
759 +[MainVolumeContext x-example-default-main-volume-context]
760 +description = Default main volume context
761 +main-output-volume-control = bind:AudioGroup:x-example-default-output-audio-group
762 +main-input-volume-control = bind:AudioGroup:x-example-default-input-audio-group
763 +main-output-mute-control = none
764 +main-input-mute-control = none
765 diff --git a/src/modules/main-volume-policy/main-volume-policy.h b/src/modules/main-volume-policy/main-volume-policy.h
766 new file mode 100644
767 index 0000000..5cd669e
768 --- /dev/null
769 +++ b/src/modules/main-volume-policy/main-volume-policy.h
770 @@ -0,0 +1,72 @@
771 +#ifndef foomainvolumepolicyhfoo
772 +#define foomainvolumepolicyhfoo
773 +
774 +/***
775 +  This file is part of PulseAudio.
776 +
777 +  Copyright 2014 Intel Corporation
778 +
779 +  PulseAudio is free software; you can redistribute it and/or modify
780 +  it under the terms of the GNU Lesser General Public License as published
781 +  by the Free Software Foundation; either version 2.1 of the License,
782 +  or (at your option) any later version.
783 +
784 +  PulseAudio is distributed in the hope that it will be useful, but
785 +  WITHOUT ANY WARRANTY; without even the implied warranty of
786 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
787 +  General Public License for more details.
788 +
789 +  You should have received a copy of the GNU Lesser General Public License
790 +  along with PulseAudio; if not, write to the Free Software
791 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
792 +  USA.
793 +***/
794 +
795 +#include <modules/volume-api/binding.h>
796 +#include <modules/volume-api/volume-api.h>
797 +
798 +#include <pulsecore/core.h>
799 +
800 +typedef struct pa_main_volume_policy pa_main_volume_policy;
801 +
802 +/* Avoid circular dependencies... */
803 +typedef struct pa_main_volume_context pa_main_volume_context;
804 +
805 +enum {
806 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT,
807 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK,
808 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
809 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED,
810 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED,
811 +    PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED,
812 +    PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED,
813 +    PA_MAIN_VOLUME_POLICY_HOOK_MAX,
814 +};
815 +
816 +struct pa_main_volume_policy {
817 +    pa_core *core;
818 +    unsigned refcnt;
819 +    pa_volume_api *volume_api;
820 +    pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */
821 +    pa_hashmap *main_volume_contexts; /* name -> pa_main_volume_context */
822 +    pa_main_volume_context *active_main_volume_context;
823 +
824 +    uint32_t next_main_volume_context_index;
825 +    pa_hook hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAX];
826 +    pa_binding_target_type *main_volume_context_binding_target_type;
827 +};
828 +
829 +pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core);
830 +pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy);
831 +void pa_main_volume_policy_unref(pa_main_volume_policy *policy);
832 +
833 +int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const char *requested_name,
834 +                                        bool fail_if_already_registered, const char **registered_name);
835 +void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name);
836 +
837 +uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy);
838 +void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context);
839 +int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context);
840 +void pa_main_volume_policy_set_active_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context);
841 +
842 +#endif
843 diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c
844 new file mode 100644
845 index 0000000..a14699d
846 --- /dev/null
847 +++ b/src/modules/main-volume-policy/module-main-volume-policy.c
848 @@ -0,0 +1,556 @@
849 +/***
850 +  This file is part of PulseAudio.
851 +
852 +  Copyright 2014 Intel Corporation
853 +
854 +  PulseAudio is free software; you can redistribute it and/or modify
855 +  it under the terms of the GNU Lesser General Public License as published
856 +  by the Free Software Foundation; either version 2.1 of the License,
857 +  or (at your option) any later version.
858 +
859 +  PulseAudio is distributed in the hope that it will be useful, but
860 +  WITHOUT ANY WARRANTY; without even the implied warranty of
861 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
862 +  General Public License for more details.
863 +
864 +  You should have received a copy of the GNU Lesser General Public License
865 +  along with PulseAudio; if not, write to the Free Software
866 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
867 +  USA.
868 +***/
869 +
870 +#ifdef HAVE_CONFIG_H
871 +#include <config.h>
872 +#endif
873 +
874 +#include "module-main-volume-policy-symdef.h"
875 +
876 +#include <modules/main-volume-policy/main-volume-context.h>
877 +
878 +#include <modules/volume-api/binding.h>
879 +#include <modules/volume-api/volume-api.h>
880 +
881 +#include <pulse/direction.h>
882 +
883 +#include <pulsecore/conf-parser.h>
884 +#include <pulsecore/core-util.h>
885 +#include <pulsecore/i18n.h>
886 +
887 +PA_MODULE_AUTHOR("Tanu Kaskinen");
888 +PA_MODULE_DESCRIPTION(_("Main volume and mute policy"));
889 +PA_MODULE_VERSION(PACKAGE_VERSION);
890 +PA_MODULE_LOAD_ONCE(true);
891 +
892 +enum control_type {
893 +    CONTROL_TYPE_VOLUME,
894 +    CONTROL_TYPE_MUTE,
895 +};
896 +
897 +enum model {
898 +    MODEL_NONE,
899 +    MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT,
900 +};
901 +
902 +struct userdata {
903 +    pa_main_volume_policy *main_volume_policy;
904 +    enum model output_volume_model;
905 +    enum model input_volume_model;
906 +    enum model output_mute_model;
907 +    enum model input_mute_model;
908 +    pa_hashmap *contexts; /* name -> struct context */
909 +
910 +    pa_hook_slot *active_main_volume_context_changed_slot;
911 +
912 +    /* The following fields are only used during initialization. */
913 +    pa_hashmap *context_names; /* name -> name (hashmap-as-a-set) */
914 +    pa_hashmap *unused_contexts; /* name -> struct context */
915 +};
916 +
917 +struct context {
918 +    struct userdata *userdata;
919 +    char *name;
920 +    char *description;
921 +    pa_binding_target_info *main_output_volume_control_target_info;
922 +    pa_binding_target_info *main_input_volume_control_target_info;
923 +    pa_binding_target_info *main_output_mute_control_target_info;
924 +    pa_binding_target_info *main_input_mute_control_target_info;
925 +    pa_main_volume_context *main_volume_context;
926 +
927 +    bool unlinked;
928 +};
929 +
930 +static void context_unlink(struct context *context);
931 +
932 +static const char *model_to_string(enum model model) {
933 +    switch (model) {
934 +        case MODEL_NONE:
935 +            return "none";
936 +
937 +        case MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT:
938 +            return "by-active-main-volume-context";
939 +    }
940 +
941 +    pa_assert_not_reached();
942 +}
943 +
944 +static int model_from_string(const char *str, enum model *model) {
945 +    pa_assert(str);
946 +    pa_assert(model);
947 +
948 +    if (pa_streq(str, "none"))
949 +        *model = MODEL_NONE;
950 +    else if (pa_streq(str, "by-active-main-volume-context"))
951 +        *model = MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT;
952 +    else
953 +        return -PA_ERR_INVALID;
954 +
955 +    return 0;
956 +}
957 +
958 +static struct context *context_new(struct userdata *u, const char *name) {
959 +    struct context *context;
960 +
961 +    pa_assert(u);
962 +    pa_assert(name);
963 +
964 +    context = pa_xnew0(struct context, 1);
965 +    context->userdata = u;
966 +    context->name = pa_xstrdup(name);
967 +    context->description = pa_xstrdup(name);
968 +
969 +    return context;
970 +}
971 +
972 +static int context_put(struct context *context) {
973 +    int r;
974 +
975 +    pa_assert(context);
976 +
977 +    r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description,
978 +                                   &context->main_volume_context);
979 +    if (r < 0)
980 +        goto fail;
981 +
982 +    if (context->main_output_volume_control_target_info)
983 +        pa_main_volume_context_bind_main_output_volume_control(context->main_volume_context,
984 +                                                               context->main_output_volume_control_target_info);
985 +
986 +    if (context->main_input_volume_control_target_info)
987 +        pa_main_volume_context_bind_main_input_volume_control(context->main_volume_context,
988 +                                                              context->main_input_volume_control_target_info);
989 +
990 +    if (context->main_output_mute_control_target_info)
991 +        pa_main_volume_context_bind_main_output_mute_control(context->main_volume_context,
992 +                                                             context->main_output_mute_control_target_info);
993 +
994 +    if (context->main_input_mute_control_target_info)
995 +        pa_main_volume_context_bind_main_input_mute_control(context->main_volume_context,
996 +                                                            context->main_input_mute_control_target_info);
997 +
998 +    pa_main_volume_context_put(context->main_volume_context);
999 +
1000 +    return 0;
1001 +
1002 +fail:
1003 +    context_unlink(context);
1004 +
1005 +    return r;
1006 +}
1007 +
1008 +static void context_unlink(struct context *context) {
1009 +    pa_assert(context);
1010 +
1011 +    if (context->unlinked)
1012 +        return;
1013 +
1014 +    context->unlinked = true;
1015 +
1016 +    if (context->main_volume_context) {
1017 +        pa_main_volume_context_free(context->main_volume_context);
1018 +        context->main_volume_context = NULL;
1019 +    }
1020 +}
1021 +
1022 +static void context_free(struct context *context) {
1023 +    pa_assert(context);
1024 +
1025 +    if (!context->unlinked)
1026 +        context_unlink(context);
1027 +
1028 +    if (context->main_input_mute_control_target_info)
1029 +        pa_binding_target_info_free(context->main_input_mute_control_target_info);
1030 +
1031 +    if (context->main_output_mute_control_target_info)
1032 +        pa_binding_target_info_free(context->main_output_mute_control_target_info);
1033 +
1034 +    if (context->main_input_volume_control_target_info)
1035 +        pa_binding_target_info_free(context->main_input_volume_control_target_info);
1036 +
1037 +    if (context->main_output_volume_control_target_info)
1038 +        pa_binding_target_info_free(context->main_output_volume_control_target_info);
1039 +
1040 +    pa_xfree(context->description);
1041 +    pa_xfree(context->name);
1042 +    pa_xfree(context);
1043 +}
1044 +
1045 +static void context_set_description(struct context *context, const char *description) {
1046 +    pa_assert(context);
1047 +    pa_assert(description);
1048 +
1049 +    pa_xfree(context->description);
1050 +    context->description = pa_xstrdup(description);
1051 +}
1052 +
1053 +static void context_set_main_control_target_info(struct context *context, enum control_type type, pa_direction_t direction,
1054 +                                                 pa_binding_target_info *info) {
1055 +    pa_assert(context);
1056 +
1057 +    switch (type) {
1058 +        case CONTROL_TYPE_VOLUME:
1059 +            if (direction == PA_DIRECTION_OUTPUT) {
1060 +                if (context->main_output_volume_control_target_info)
1061 +                    pa_binding_target_info_free(context->main_output_volume_control_target_info);
1062 +
1063 +                if (info)
1064 +                    context->main_output_volume_control_target_info = pa_binding_target_info_copy(info);
1065 +                else
1066 +                    context->main_output_volume_control_target_info = NULL;
1067 +            } else {
1068 +                if (context->main_input_volume_control_target_info)
1069 +                    pa_binding_target_info_free(context->main_input_volume_control_target_info);
1070 +
1071 +                if (info)
1072 +                    context->main_input_volume_control_target_info = pa_binding_target_info_copy(info);
1073 +                else
1074 +                    context->main_input_volume_control_target_info = NULL;
1075 +            }
1076 +            break;
1077 +
1078 +        case CONTROL_TYPE_MUTE:
1079 +            if (direction == PA_DIRECTION_OUTPUT) {
1080 +                if (context->main_output_mute_control_target_info)
1081 +                    pa_binding_target_info_free(context->main_output_mute_control_target_info);
1082 +
1083 +                if (info)
1084 +                    context->main_output_mute_control_target_info = pa_binding_target_info_copy(info);
1085 +                else
1086 +                    context->main_output_mute_control_target_info = NULL;
1087 +            } else {
1088 +                if (context->main_input_mute_control_target_info)
1089 +                    pa_binding_target_info_free(context->main_input_mute_control_target_info);
1090 +
1091 +                if (info)
1092 +                    context->main_input_mute_control_target_info = pa_binding_target_info_copy(info);
1093 +                else
1094 +                    context->main_input_mute_control_target_info = NULL;
1095 +            }
1096 +            break;
1097 +    }
1098 +}
1099 +
1100 +static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, void *call_data, void *userdata) {
1101 +    struct userdata *u = userdata;
1102 +    pa_main_volume_context *context;
1103 +    pa_volume_api *api;
1104 +    pa_binding_target_info *info = NULL;
1105 +
1106 +    pa_assert(u);
1107 +
1108 +    context = u->main_volume_policy->active_main_volume_context;
1109 +    api = u->main_volume_policy->volume_api;
1110 +
1111 +    if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
1112 +        if (context) {
1113 +            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
1114 +                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL);
1115 +            pa_volume_api_bind_main_output_volume_control(api, info);
1116 +        } else
1117 +            pa_volume_api_set_main_output_volume_control(api, NULL);
1118 +    }
1119 +
1120 +    if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
1121 +        if (context) {
1122 +            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
1123 +                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL);
1124 +            pa_volume_api_bind_main_input_volume_control(api, info);
1125 +        } else
1126 +            pa_volume_api_set_main_input_volume_control(api, NULL);
1127 +    }
1128 +
1129 +    if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
1130 +        if (context) {
1131 +            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
1132 +                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL);
1133 +            pa_volume_api_bind_main_output_mute_control(api, info);
1134 +        } else
1135 +            pa_volume_api_set_main_output_mute_control(api, NULL);
1136 +    }
1137 +
1138 +    if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
1139 +        if (context) {
1140 +            info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
1141 +                                              PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL);
1142 +            pa_volume_api_bind_main_input_mute_control(api, info);
1143 +        } else
1144 +            pa_volume_api_set_main_input_mute_control(api, NULL);
1145 +    }
1146 +
1147 +    if (info)
1148 +        pa_binding_target_info_free(info);
1149 +
1150 +    return PA_HOOK_OK;
1151 +}
1152 +
1153 +static int parse_model(pa_config_parser_state *state) {
1154 +    int r;
1155 +
1156 +    pa_assert(state);
1157 +
1158 +    r = model_from_string(state->rvalue, state->data);
1159 +    if (r < 0)
1160 +        pa_log("[%s:%u] Failed to parse model: %s", state->filename, state->lineno, state->rvalue);
1161 +
1162 +    return r;
1163 +}
1164 +
1165 +static int parse_main_volume_contexts(pa_config_parser_state *state) {
1166 +    struct userdata *u;
1167 +    char *name;
1168 +    const char *split_state = NULL;
1169 +
1170 +    pa_assert(state);
1171 +
1172 +    u = state->userdata;
1173 +
1174 +    while ((name = pa_split_spaces(state->rvalue, &split_state)))
1175 +        pa_hashmap_put(u->context_names, name, name);
1176 +
1177 +    return 0;
1178 +}
1179 +
1180 +static struct context *get_context(struct userdata *u, const char *section) {
1181 +    const char *name;
1182 +    struct context *context;
1183 +
1184 +    pa_assert(u);
1185 +
1186 +    if (!section)
1187 +        return NULL;
1188 +
1189 +    if (!pa_startswith(section, "MainVolumeContext "))
1190 +        return NULL;
1191 +
1192 +    name = section + 18;
1193 +
1194 +    context = pa_hashmap_get(u->unused_contexts, name);
1195 +    if (!context) {
1196 +        context = context_new(u, name);
1197 +        pa_hashmap_put(u->unused_contexts, context->name, context);
1198 +    }
1199 +
1200 +    return context;
1201 +}
1202 +
1203 +static int parse_description(pa_config_parser_state *state) {
1204 +    struct userdata *u;
1205 +    struct context *context;
1206 +
1207 +    pa_assert(state);
1208 +
1209 +    u = state->userdata;
1210 +
1211 +    context = get_context(u, state->section);
1212 +    if (!context) {
1213 +        pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
1214 +               pa_strnull(state->section));
1215 +        return -PA_ERR_INVALID;
1216 +    }
1217 +
1218 +    context_set_description(context, state->rvalue);
1219 +
1220 +    return 0;
1221 +}
1222 +
1223 +static const char *get_target_field_name(enum control_type type) {
1224 +    switch (type) {
1225 +        case CONTROL_TYPE_VOLUME:
1226 +            return "volume_control";
1227 +
1228 +        case CONTROL_TYPE_MUTE:
1229 +            return "mute_control";
1230 +    }
1231 +
1232 +    pa_assert_not_reached();
1233 +}
1234 +
1235 +static int parse_main_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
1236 +    struct userdata *u;
1237 +    struct context *context;
1238 +
1239 +    pa_assert(state);
1240 +
1241 +    u = state->userdata;
1242 +
1243 +    context = get_context(u, state->section);
1244 +    if (!context) {
1245 +        pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
1246 +               pa_strnull(state->section));
1247 +        return -PA_ERR_INVALID;
1248 +    }
1249 +
1250 +    if (pa_streq(state->rvalue, "none"))
1251 +        context_set_main_control_target_info(context, type, direction, NULL);
1252 +    else if (pa_startswith(state->rvalue, "bind:")) {
1253 +        int r;
1254 +        pa_binding_target_info *info;
1255 +
1256 +        r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info);
1257 +        if (r < 0) {
1258 +            pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
1259 +            return r;
1260 +        }
1261 +
1262 +        context_set_main_control_target_info(context, type, direction, info);
1263 +        pa_binding_target_info_free(info);
1264 +    } else {
1265 +        pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1266 +        return -PA_ERR_INVALID;
1267 +    }
1268 +
1269 +    return 0;
1270 +}
1271 +
1272 +static int parse_main_output_volume_control(pa_config_parser_state *state) {
1273 +    pa_assert(state);
1274 +
1275 +    return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
1276 +}
1277 +
1278 +static int parse_main_input_volume_control(pa_config_parser_state *state) {
1279 +    pa_assert(state);
1280 +
1281 +    return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
1282 +}
1283 +
1284 +static int parse_main_output_mute_control(pa_config_parser_state *state) {
1285 +    pa_assert(state);
1286 +
1287 +    return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
1288 +}
1289 +
1290 +static int parse_main_input_mute_control(pa_config_parser_state *state) {
1291 +    pa_assert(state);
1292 +
1293 +    return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
1294 +}
1295 +
1296 +static void finalize_config(struct userdata *u) {
1297 +    const char *context_name;
1298 +    void *state;
1299 +    struct context *context;
1300 +
1301 +    pa_assert(u);
1302 +
1303 +    PA_HASHMAP_FOREACH(context_name, u->context_names, state) {
1304 +        int r;
1305 +
1306 +        context = pa_hashmap_remove(u->unused_contexts, context_name);
1307 +        if (!context)
1308 +            context = context_new(u, context_name);
1309 +
1310 +        r = context_put(context);
1311 +        if (r < 0) {
1312 +            pa_log_warn("Failed to create main volume context %s.", context_name);
1313 +            context_free(context);
1314 +            continue;
1315 +        }
1316 +
1317 +        pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0);
1318 +    }
1319 +
1320 +    PA_HASHMAP_FOREACH(context, u->unused_contexts, state)
1321 +        pa_log_debug("Main volume context %s is not used.", context->name);
1322 +
1323 +    pa_hashmap_free(u->unused_contexts);
1324 +    u->unused_contexts = NULL;
1325 +
1326 +    pa_hashmap_free(u->context_names);
1327 +    u->context_names = NULL;
1328 +}
1329 +
1330 +int pa__init(pa_module *module) {
1331 +    struct userdata *u;
1332 +    FILE *f;
1333 +    char *fn = NULL;
1334 +
1335 +    pa_assert(module);
1336 +
1337 +    u = module->userdata = pa_xnew0(struct userdata, 1);
1338 +    u->main_volume_policy = pa_main_volume_policy_get(module->core);
1339 +    u->output_volume_model = MODEL_NONE;
1340 +    u->input_volume_model = MODEL_NONE;
1341 +    u->output_mute_model = MODEL_NONE;
1342 +    u->input_mute_model = MODEL_NONE;
1343 +    u->contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1344 +                                      (pa_free_cb_t) context_free);
1345 +    u->active_main_volume_context_changed_slot =
1346 +            pa_hook_connect(&u->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED],
1347 +                            PA_HOOK_NORMAL, active_main_volume_context_changed_cb, u);
1348 +    u->context_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1349 +    u->unused_contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1350 +                                             (pa_free_cb_t) context_free);
1351 +
1352 +    f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn);
1353 +    if (f) {
1354 +        pa_config_item config_items[] = {
1355 +            { "output-volume-model", parse_model, &u->output_volume_model, "General" },
1356 +            { "input-volume-model", parse_model, &u->input_volume_model, "General" },
1357 +            { "output-mute-model", parse_model, &u->output_mute_model, "General" },
1358 +            { "input-mute-model", parse_model, &u->input_mute_model, "General" },
1359 +            { "main-volume-contexts", parse_main_volume_contexts, NULL, "General" },
1360 +            { "description", parse_description, NULL, NULL },
1361 +            { "main-output-volume-control", parse_main_output_volume_control, NULL, NULL },
1362 +            { "main-input-volume-control", parse_main_input_volume_control, NULL, NULL },
1363 +            { "main-output-mute-control", parse_main_output_mute_control, NULL, NULL },
1364 +            { "main-input-mute-control", parse_main_input_mute_control, NULL, NULL },
1365 +            { NULL },
1366 +        };
1367 +
1368 +        pa_config_parse(fn, f, config_items, NULL, u);
1369 +        pa_xfree(fn);
1370 +        fn = NULL;
1371 +        fclose(f);
1372 +        f = NULL;
1373 +    }
1374 +
1375 +    finalize_config(u);
1376 +
1377 +    pa_log_debug("Output volume model: %s", model_to_string(u->output_volume_model));
1378 +    pa_log_debug("Input volume model: %s", model_to_string(u->input_volume_model));
1379 +    pa_log_debug("Output mute model: %s", model_to_string(u->output_mute_model));
1380 +    pa_log_debug("Input mute model: %s", model_to_string(u->input_mute_model));
1381 +
1382 +    return 0;
1383 +}
1384 +
1385 +void pa__done(pa_module *module) {
1386 +    struct userdata *u;
1387 +
1388 +    pa_assert(module);
1389 +
1390 +    u = module->userdata;
1391 +    if (!u)
1392 +        return;
1393 +
1394 +    if (u->active_main_volume_context_changed_slot)
1395 +        pa_hook_slot_free(u->active_main_volume_context_changed_slot);
1396 +
1397 +    if (u->contexts)
1398 +        pa_hashmap_free(u->contexts);
1399 +
1400 +    if (u->main_volume_policy)
1401 +        pa_main_volume_policy_unref(u->main_volume_policy);
1402 +
1403 +    pa_xfree(u);
1404 +}