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