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
5 Change-Id: I787141b43cafb652aa752c64ae28b6b7aa052d8e
6 Signed-off-by: Jaska Uimonen <jaska.uimonen@intel.com>
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
25 diff --git a/Makefile.am b/Makefile.am
26 index cf4a648..646b7fc 100644
29 @@ -61,6 +61,9 @@ moduledevinternaldir = $(includedir)/pulsemodule/pulse
30 moduledevvolumeapi_DATA = src/modules/volume-api/*.h
31 moduledevvolumeapidir = $(includedir)/pulsemodule/modules/volume-api
33 +moduledevmainvolumepolicy_DATA = src/modules/main-volume-policy/*.h
34 +moduledevmainvolumepolicydir = $(includedir)/pulsemodule/modules/main-volume-policy
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
43 @@ -1017,6 +1017,7 @@ libpulsecore_foreign_la_CFLAGS = $(AM_CFLAGS) $(FOREIGN_CFLAGS)
45 modlibexec_LTLIBRARIES = \
47 + libmain-volume-policy.la \
49 libprotocol-simple.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
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
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 += \
67 module-cli-protocol-tcp.la \
68 + module-main-volume-policy.la \
69 module-simple-protocol-tcp.la \
70 module-volume-api.la \
72 @@ -1426,6 +1434,7 @@ SYMDEF_FILES = \
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
84 +# Main volume and mute policy
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
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
101 +.ifexists module-main-volume-policy
102 +load-module module-main-volume-policy
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
110 index 0000000..7ac35c6
112 +++ b/src/modules/main-volume-policy/main-volume-context.c
115 + This file is part of PulseAudio.
117 + Copyright 2014 Intel Corporation
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.
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.
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
135 +#ifdef HAVE_CONFIG_H
139 +#include "main-volume-context.h"
141 +#include <modules/volume-api/mute-control.h>
142 +#include <modules/volume-api/volume-control.h>
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;
151 + pa_assert(description);
152 + pa_assert(context);
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);
158 + r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name);
162 + context_local->description = pa_xstrdup(description);
164 + *context = context_local;
169 + pa_main_volume_context_free(context_local);
174 +void pa_main_volume_context_put(pa_main_volume_context *context) {
175 + pa_assert(context);
177 + pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context);
179 + context->linked = true;
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)");
193 + pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT], context);
196 +void pa_main_volume_context_unlink(pa_main_volume_context *context) {
197 + pa_assert(context);
199 + if (context->unlinked) {
200 + pa_log_debug("Unlinking main volume context %s (already unlinked, this is a no-op).", context->name);
204 + context->unlinked = true;
206 + pa_log_debug("Unlinking main volume context %s.", context->name);
208 + if (context->linked)
209 + pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
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;
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;
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;
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;
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;
236 + pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
239 +void pa_main_volume_context_free(pa_main_volume_context *context) {
240 + pa_assert(context);
242 + if (!context->unlinked)
243 + pa_main_volume_context_unlink(context);
245 + pa_xfree(context->description);
248 + pa_main_volume_policy_unregister_name(context->main_volume_policy, context->name);
253 +const char *pa_main_volume_context_get_name(pa_main_volume_context *context) {
254 + pa_assert(context);
256 + return context->name;
259 +static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
260 + pa_volume_control *old_control;
262 + pa_assert(context);
264 + old_control = context->main_output_volume_control;
266 + if (control == old_control)
269 + context->main_output_volume_control = control;
271 + if (!context->linked || context->unlinked)
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)");
277 + pa_hook_fire(&context->main_volume_policy->hooks
278 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED],
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,
289 + pa_assert(context);
290 + pa_assert(target_info);
292 + if (context->main_output_volume_control_binding)
293 + pa_binding_free(context->main_output_volume_control_binding);
295 + context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
299 +static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
300 + pa_volume_control *old_control;
302 + pa_assert(context);
304 + old_control = context->main_input_volume_control;
306 + if (control == old_control)
309 + context->main_input_volume_control = control;
311 + if (!context->linked || context->unlinked)
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)");
317 + pa_hook_fire(&context->main_volume_policy->hooks
318 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED],
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,
329 + pa_assert(context);
330 + pa_assert(target_info);
332 + if (context->main_input_volume_control_binding)
333 + pa_binding_free(context->main_input_volume_control_binding);
335 + context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
339 +static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
340 + pa_mute_control *old_control;
342 + pa_assert(context);
344 + old_control = context->main_output_mute_control;
346 + if (control == old_control)
349 + context->main_output_mute_control = control;
351 + if (!context->linked || context->unlinked)
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)");
357 + pa_hook_fire(&context->main_volume_policy->hooks
358 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED],
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,
369 + pa_assert(context);
370 + pa_assert(target_info);
372 + if (context->main_output_mute_control_binding)
373 + pa_binding_free(context->main_output_mute_control_binding);
375 + context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
379 +static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
380 + pa_mute_control *old_control;
382 + pa_assert(context);
384 + old_control = context->main_input_mute_control;
386 + if (control == old_control)
389 + context->main_input_mute_control = control;
391 + if (!context->linked || context->unlinked)
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)");
397 + pa_hook_fire(&context->main_volume_policy->hooks
398 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
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,
409 + pa_assert(context);
410 + pa_assert(target_info);
412 + if (context->main_input_mute_control_binding)
413 + pa_binding_free(context->main_input_mute_control_binding);
415 + context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
419 +pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) {
420 + pa_binding_target_type *type;
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));
439 diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h
441 index 0000000..4a0a6f7
443 +++ b/src/modules/main-volume-policy/main-volume-context.h
445 +#ifndef foomainvolumecontexthfoo
446 +#define foomainvolumecontexthfoo
449 + This file is part of PulseAudio.
451 + Copyright 2014 Intel Corporation
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.
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.
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
469 +#include <modules/main-volume-policy/main-volume-policy.h>
471 +#include <modules/volume-api/binding.h>
473 +typedef struct pa_main_volume_context pa_main_volume_context;
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"
481 +struct pa_main_volume_context {
482 + pa_main_volume_policy *main_volume_policy;
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;
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;
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);
506 +const char *pa_main_volume_context_get_name(pa_main_volume_context *context);
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);
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);
520 diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c
522 index 0000000..b0b4ede
524 +++ b/src/modules/main-volume-policy/main-volume-policy.c
527 + This file is part of PulseAudio.
529 + Copyright 2014 Intel Corporation
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.
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.
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
547 +#ifdef HAVE_CONFIG_H
551 +#include "main-volume-policy.h"
553 +#include <modules/main-volume-policy/main-volume-context.h>
555 +#include <pulsecore/core-util.h>
556 +#include <pulsecore/shared.h>
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);
561 +pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core) {
562 + pa_main_volume_policy *policy;
566 + policy = pa_shared_get(core, "main-volume-policy");
569 + pa_main_volume_policy_ref(policy);
571 + policy = main_volume_policy_new(core);
572 + pa_assert_se(pa_shared_set(core, "main-volume-policy", policy) >= 0);
578 +pa_main_volume_policy *pa_main_volume_policy_ref(pa_main_volume_policy *policy) {
586 +void pa_main_volume_policy_unref(pa_main_volume_policy *policy) {
588 + pa_assert(policy->refcnt > 0);
592 + if (policy->refcnt == 0) {
593 + pa_assert_se(pa_shared_remove(policy->core, "main-volume-policy") >= 0);
594 + main_volume_policy_free(policy);
598 +static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
599 + pa_main_volume_policy *policy;
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);
611 + for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
612 + pa_hook_init(&policy->hooks[i], policy);
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);
617 + pa_log_debug("Created a pa_main_volume_policy object.");
622 +static void main_volume_policy_free(pa_main_volume_policy *policy) {
626 + pa_assert(policy->refcnt == 0);
628 + pa_log_debug("Freeing the pa_main_volume_policy object.");
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);
635 + for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
636 + pa_hook_done(&policy->hooks[i]);
638 + if (policy->main_volume_contexts) {
639 + pa_assert(pa_hashmap_isempty(policy->main_volume_contexts));
640 + pa_hashmap_free(policy->main_volume_contexts);
643 + if (policy->names) {
644 + pa_assert(pa_hashmap_isempty(policy->names));
645 + pa_hashmap_free(policy->names);
648 + if (policy->volume_api)
649 + pa_volume_api_unref(policy->volume_api);
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) {
659 + pa_assert(requested_name);
660 + pa_assert(registered_name);
662 + n = pa_xstrdup(requested_name);
664 + if (pa_hashmap_put(policy->names, n, n) < 0) {
669 + if (fail_if_already_registered) {
670 + pa_log("Name %s already registered.", requested_name);
671 + return -PA_ERR_EXIST;
676 + n = pa_sprintf_malloc("%s.%u", requested_name, i);
677 + } while (pa_hashmap_put(policy->names, n, n) < 0);
680 + *registered_name = n;
685 +void pa_main_volume_policy_unregister_name(pa_main_volume_policy *policy, const char *name) {
689 + pa_assert_se(pa_hashmap_remove_and_free(policy->names, name) >= 0);
692 +uint32_t pa_main_volume_policy_allocate_main_volume_context_index(pa_main_volume_policy *policy) {
697 + idx = policy->next_main_volume_context_index++;
702 +void pa_main_volume_policy_add_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
704 + pa_assert(context);
706 + pa_assert_se(pa_hashmap_put(policy->main_volume_contexts, (void *) context->name, context) >= 0);
709 +int pa_main_volume_policy_remove_main_volume_context(pa_main_volume_policy *policy, pa_main_volume_context *context) {
711 + pa_assert(context);
713 + if (!pa_hashmap_remove(policy->main_volume_contexts, context->name))
716 + if (context == policy->active_main_volume_context)
717 + pa_main_volume_policy_set_active_main_volume_context(policy, NULL);
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;
727 + old_context = policy->active_main_volume_context;
729 + if (context == old_context)
732 + policy->active_main_volume_context = context;
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)");
737 + pa_hook_fire(&policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED], NULL);
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
741 index 0000000..a4a35d3
743 +++ b/src/modules/main-volume-policy/main-volume-policy.conf.example
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
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
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
767 index 0000000..5cd669e
769 +++ b/src/modules/main-volume-policy/main-volume-policy.h
771 +#ifndef foomainvolumepolicyhfoo
772 +#define foomainvolumepolicyhfoo
775 + This file is part of PulseAudio.
777 + Copyright 2014 Intel Corporation
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.
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.
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
795 +#include <modules/volume-api/binding.h>
796 +#include <modules/volume-api/volume-api.h>
798 +#include <pulsecore/core.h>
800 +typedef struct pa_main_volume_policy pa_main_volume_policy;
802 +/* Avoid circular dependencies... */
803 +typedef struct pa_main_volume_context pa_main_volume_context;
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,
816 +struct pa_main_volume_policy {
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;
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;
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);
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);
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);
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
845 index 0000000..a14699d
847 +++ b/src/modules/main-volume-policy/module-main-volume-policy.c
850 + This file is part of PulseAudio.
852 + Copyright 2014 Intel Corporation
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.
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.
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
870 +#ifdef HAVE_CONFIG_H
874 +#include "module-main-volume-policy-symdef.h"
876 +#include <modules/main-volume-policy/main-volume-context.h>
878 +#include <modules/volume-api/binding.h>
879 +#include <modules/volume-api/volume-api.h>
881 +#include <pulse/direction.h>
883 +#include <pulsecore/conf-parser.h>
884 +#include <pulsecore/core-util.h>
885 +#include <pulsecore/i18n.h>
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);
893 + CONTROL_TYPE_VOLUME,
899 + MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT,
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 */
910 + pa_hook_slot *active_main_volume_context_changed_slot;
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 */
918 + struct userdata *userdata;
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;
930 +static void context_unlink(struct context *context);
932 +static const char *model_to_string(enum model model) {
937 + case MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT:
938 + return "by-active-main-volume-context";
941 + pa_assert_not_reached();
944 +static int model_from_string(const char *str, enum model *model) {
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;
953 + return -PA_ERR_INVALID;
958 +static struct context *context_new(struct userdata *u, const char *name) {
959 + struct context *context;
964 + context = pa_xnew0(struct context, 1);
965 + context->userdata = u;
966 + context->name = pa_xstrdup(name);
967 + context->description = pa_xstrdup(name);
972 +static int context_put(struct context *context) {
975 + pa_assert(context);
977 + r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description,
978 + &context->main_volume_context);
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);
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);
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);
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);
998 + pa_main_volume_context_put(context->main_volume_context);
1003 + context_unlink(context);
1008 +static void context_unlink(struct context *context) {
1009 + pa_assert(context);
1011 + if (context->unlinked)
1014 + context->unlinked = true;
1016 + if (context->main_volume_context) {
1017 + pa_main_volume_context_free(context->main_volume_context);
1018 + context->main_volume_context = NULL;
1022 +static void context_free(struct context *context) {
1023 + pa_assert(context);
1025 + if (!context->unlinked)
1026 + context_unlink(context);
1028 + if (context->main_input_mute_control_target_info)
1029 + pa_binding_target_info_free(context->main_input_mute_control_target_info);
1031 + if (context->main_output_mute_control_target_info)
1032 + pa_binding_target_info_free(context->main_output_mute_control_target_info);
1034 + if (context->main_input_volume_control_target_info)
1035 + pa_binding_target_info_free(context->main_input_volume_control_target_info);
1037 + if (context->main_output_volume_control_target_info)
1038 + pa_binding_target_info_free(context->main_output_volume_control_target_info);
1040 + pa_xfree(context->description);
1041 + pa_xfree(context->name);
1042 + pa_xfree(context);
1045 +static void context_set_description(struct context *context, const char *description) {
1046 + pa_assert(context);
1047 + pa_assert(description);
1049 + pa_xfree(context->description);
1050 + context->description = pa_xstrdup(description);
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);
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);
1064 + context->main_output_volume_control_target_info = pa_binding_target_info_copy(info);
1066 + context->main_output_volume_control_target_info = NULL;
1068 + if (context->main_input_volume_control_target_info)
1069 + pa_binding_target_info_free(context->main_input_volume_control_target_info);
1072 + context->main_input_volume_control_target_info = pa_binding_target_info_copy(info);
1074 + context->main_input_volume_control_target_info = NULL;
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);
1084 + context->main_output_mute_control_target_info = pa_binding_target_info_copy(info);
1086 + context->main_output_mute_control_target_info = NULL;
1088 + if (context->main_input_mute_control_target_info)
1089 + pa_binding_target_info_free(context->main_input_mute_control_target_info);
1092 + context->main_input_mute_control_target_info = pa_binding_target_info_copy(info);
1094 + context->main_input_mute_control_target_info = NULL;
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;
1108 + context = u->main_volume_policy->active_main_volume_context;
1109 + api = u->main_volume_policy->volume_api;
1111 + if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_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);
1117 + pa_volume_api_set_main_output_volume_control(api, NULL);
1120 + if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_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);
1126 + pa_volume_api_set_main_input_volume_control(api, NULL);
1129 + if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_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);
1135 + pa_volume_api_set_main_output_mute_control(api, NULL);
1138 + if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_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);
1144 + pa_volume_api_set_main_input_mute_control(api, NULL);
1148 + pa_binding_target_info_free(info);
1150 + return PA_HOOK_OK;
1153 +static int parse_model(pa_config_parser_state *state) {
1158 + r = model_from_string(state->rvalue, state->data);
1160 + pa_log("[%s:%u] Failed to parse model: %s", state->filename, state->lineno, state->rvalue);
1165 +static int parse_main_volume_contexts(pa_config_parser_state *state) {
1166 + struct userdata *u;
1168 + const char *split_state = NULL;
1172 + u = state->userdata;
1174 + while ((name = pa_split_spaces(state->rvalue, &split_state)))
1175 + pa_hashmap_put(u->context_names, name, name);
1180 +static struct context *get_context(struct userdata *u, const char *section) {
1182 + struct context *context;
1189 + if (!pa_startswith(section, "MainVolumeContext "))
1192 + name = section + 18;
1194 + context = pa_hashmap_get(u->unused_contexts, name);
1196 + context = context_new(u, name);
1197 + pa_hashmap_put(u->unused_contexts, context->name, context);
1203 +static int parse_description(pa_config_parser_state *state) {
1204 + struct userdata *u;
1205 + struct context *context;
1209 + u = state->userdata;
1211 + context = get_context(u, state->section);
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;
1218 + context_set_description(context, state->rvalue);
1223 +static const char *get_target_field_name(enum control_type type) {
1225 + case CONTROL_TYPE_VOLUME:
1226 + return "volume_control";
1228 + case CONTROL_TYPE_MUTE:
1229 + return "mute_control";
1232 + pa_assert_not_reached();
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;
1241 + u = state->userdata;
1243 + context = get_context(u, state->section);
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;
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:")) {
1254 + pa_binding_target_info *info;
1256 + r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info);
1258 + pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
1262 + context_set_main_control_target_info(context, type, direction, info);
1263 + pa_binding_target_info_free(info);
1265 + pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1266 + return -PA_ERR_INVALID;
1272 +static int parse_main_output_volume_control(pa_config_parser_state *state) {
1275 + return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
1278 +static int parse_main_input_volume_control(pa_config_parser_state *state) {
1281 + return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
1284 +static int parse_main_output_mute_control(pa_config_parser_state *state) {
1287 + return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
1290 +static int parse_main_input_mute_control(pa_config_parser_state *state) {
1293 + return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
1296 +static void finalize_config(struct userdata *u) {
1297 + const char *context_name;
1299 + struct context *context;
1303 + PA_HASHMAP_FOREACH(context_name, u->context_names, state) {
1306 + context = pa_hashmap_remove(u->unused_contexts, context_name);
1308 + context = context_new(u, context_name);
1310 + r = context_put(context);
1312 + pa_log_warn("Failed to create main volume context %s.", context_name);
1313 + context_free(context);
1317 + pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0);
1320 + PA_HASHMAP_FOREACH(context, u->unused_contexts, state)
1321 + pa_log_debug("Main volume context %s is not used.", context->name);
1323 + pa_hashmap_free(u->unused_contexts);
1324 + u->unused_contexts = NULL;
1326 + pa_hashmap_free(u->context_names);
1327 + u->context_names = NULL;
1330 +int pa__init(pa_module *module) {
1331 + struct userdata *u;
1335 + pa_assert(module);
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);
1352 + f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn);
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 },
1368 + pa_config_parse(fn, f, config_items, NULL, u);
1375 + finalize_config(u);
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));
1385 +void pa__done(pa_module *module) {
1386 + struct userdata *u;
1388 + pa_assert(module);
1390 + u = module->userdata;
1394 + if (u->active_main_volume_context_changed_slot)
1395 + pa_hook_slot_free(u->active_main_volume_context_changed_slot);
1398 + pa_hashmap_free(u->contexts);
1400 + if (u->main_volume_policy)
1401 + pa_main_volume_policy_unref(u->main_volume_policy);