1 From: Tanu Kaskinen <tanu.kaskinen@linux.intel.com>
2 Date: Tue, 17 Jun 2014 19:45:45 +0300
3 Subject: audio-groups, main-volume-policy, volume-api: Various fixes
5 Sorry, this is a huge unreviewable commit. Contained improvements
8 * Flat volumes are now handled properly. Previously, audio groups
9 controlled the absolute volume of streams if flat volume was in
10 effect, which made no sense.
11 * Audio group volumes are now persistent.
12 * Audio group volumes are applied to new streams before the streams
13 start to play, instead of after, which could cause audible
15 * When a stream volume is changed by the user, the volume is
16 propagated to the stream's audio group.
17 * Fixed the handling of the "NEG" keyword in the match syntax in
18 module-audio-groups. Previously the "NEG" keyword was parsed, but
21 Change-Id: I02bad3d23b3e562c71dbc6af6f3e308089893751
23 src/Makefile.am | 2 +-
25 src/modules/audio-groups/audio-groups.conf.example | 19 +-
26 src/modules/audio-groups/module-audio-groups.c | 1710 ++++++++++++++------
27 .../main-volume-policy/main-volume-context.c | 184 +--
28 .../main-volume-policy/main-volume-context.h | 35 +-
29 .../main-volume-policy/main-volume-policy.c | 65 +-
30 .../main-volume-policy.conf.example | 1 -
31 .../main-volume-policy/main-volume-policy.h | 6 +-
32 .../main-volume-policy/module-main-volume-policy.c | 651 +++++---
33 src/modules/volume-api/audio-group.c | 288 +---
34 src/modules/volume-api/audio-group.h | 29 +-
35 src/modules/volume-api/binding.c | 386 -----
36 src/modules/volume-api/binding.h | 128 --
37 src/modules/volume-api/bvolume.h | 3 +
38 src/modules/volume-api/device-creator.c | 431 +++--
39 src/modules/volume-api/device.c | 54 +-
40 src/modules/volume-api/device.h | 4 +-
41 src/modules/volume-api/inidb.c | 553 +++++++
42 src/modules/volume-api/inidb.h | 54 +
43 src/modules/volume-api/module-volume-api.c | 27 +
44 src/modules/volume-api/mute-control.c | 241 +--
45 src/modules/volume-api/mute-control.h | 75 +-
46 src/modules/volume-api/sstream.c | 313 ++--
47 src/modules/volume-api/sstream.h | 56 +-
48 src/modules/volume-api/stream-creator.c | 691 ++++----
49 src/modules/volume-api/volume-api.c | 430 +++--
50 src/modules/volume-api/volume-api.h | 79 +-
51 src/modules/volume-api/volume-control.c | 308 ++--
52 src/modules/volume-api/volume-control.h | 93 +-
53 src/pulse/ext-volume-api.c | 100 ++
54 src/pulse/ext-volume-api.h | 3 +
55 src/tizen-ivi/audio-groups.conf | 23 +-
56 src/tizen-ivi/main-volume-policy.conf | 1 -
57 34 files changed, 4012 insertions(+), 3034 deletions(-)
58 delete mode 100644 src/modules/volume-api/binding.c
59 delete mode 100644 src/modules/volume-api/binding.h
60 create mode 100644 src/modules/volume-api/inidb.c
61 create mode 100644 src/modules/volume-api/inidb.h
63 diff --git a/src/Makefile.am b/src/Makefile.am
64 index 9d17336..b5cf2a8 100644
67 @@ -1098,10 +1098,10 @@ endif
69 libvolume_api_la_SOURCES = \
70 modules/volume-api/audio-group.c modules/volume-api/audio-group.h \
71 - modules/volume-api/binding.c modules/volume-api/binding.h \
72 modules/volume-api/bvolume.h \
73 modules/volume-api/device.c modules/volume-api/device.h \
74 modules/volume-api/device-creator.c modules/volume-api/device-creator.h \
75 + modules/volume-api/inidb.c modules/volume-api/inidb.h \
76 modules/volume-api/mute-control.c modules/volume-api/mute-control.h \
77 modules/volume-api/sstream.c modules/volume-api/sstream.h \
78 modules/volume-api/stream-creator.c modules/volume-api/stream-creator.h \
79 diff --git a/src/map-file b/src/map-file
80 index 28ea54e..cb31833 100644
83 @@ -190,13 +190,16 @@ pa_ext_node_manager_set_subscribe_cb;
84 pa_ext_echo_cancel_set_volume;
85 pa_ext_echo_cancel_set_device;
86 pa_ext_volume_api_balance_valid;
87 +pa_ext_volume_api_bvolume_balance_to_string;
88 pa_ext_volume_api_bvolume_copy_balance;
89 pa_ext_volume_api_bvolume_get_left_right_balance;
90 pa_ext_volume_api_bvolume_get_rear_front_balance;
91 pa_ext_volume_api_bvolume_equal;
92 pa_ext_volume_api_bvolume_from_cvolume;
93 +pa_ext_volume_api_bvolume_init;
94 pa_ext_volume_api_bvolume_init_invalid;
95 pa_ext_volume_api_bvolume_init_mono;
96 +pa_ext_volume_api_bvolume_parse_balance;
97 pa_ext_volume_api_bvolume_remap;
98 pa_ext_volume_api_bvolume_reset_balance;
99 pa_ext_volume_api_bvolume_set_left_right_balance;
100 diff --git a/src/modules/audio-groups/audio-groups.conf.example b/src/modules/audio-groups/audio-groups.conf.example
101 index 8acdb76..6aa6989 100644
102 --- a/src/modules/audio-groups/audio-groups.conf.example
103 +++ b/src/modules/audio-groups/audio-groups.conf.example
106 -audio-groups = x-example-call-downlink-audio-group x-example-default-output-audio-group x-example-music-output-audio-group
107 -streams = phone-output music-output default-output
108 +stream-rules = phone-output music-output default-output
110 [AudioGroup x-example-call-downlink-audio-group]
111 -volume-control = create
113 +volume-control = create:call-downlink-volume-control
114 +mute-control = create:call-downlink-mute-control
116 [AudioGroup x-example-default-output-audio-group]
117 -volume-control = create
119 +volume-control = create:default-output-volume-control
120 +mute-control = create:call-downlink-mute-control
122 [AudioGroup x-example-music-output-audio-group]
123 volume-control = bind:AudioGroup:x-example-default-output-audio-group
124 +mute-control = bind:AudioGroup:x-example-default-output-audio-group
126 -[Stream phone-output]
127 +[StreamRule phone-output]
128 match = (direction output AND property media.role=phone)
129 audio-group-for-volume = x-example-call-downlink-audio-group
130 audio-group-for-mute = x-example-call-downlink-audio-group
132 -[Stream music-output]
133 +[StreamRule music-output]
134 match = (direction output AND property media.role=music)
135 audio-group-for-volume = x-example-music-output-audio-group
136 audio-group-for-mute = x-example-music-output-audio-group
138 -[Stream default-output]
139 +[StreamRule default-output]
140 +match = (direction output)
141 audio-group-for-volume = x-example-default-output-audio-group
142 audio-group-for-mute = x-example-default-output-audio-group
143 diff --git a/src/modules/audio-groups/module-audio-groups.c b/src/modules/audio-groups/module-audio-groups.c
144 index 2b3a570..e37a24e 100644
145 --- a/src/modules/audio-groups/module-audio-groups.c
146 +++ b/src/modules/audio-groups/module-audio-groups.c
149 #include "module-audio-groups-symdef.h"
151 +#define AUDIOGROUP_START "AudioGroup "
152 +#define STREAM_RULE_START "StreamRule "
153 +#define NONE_KEYWORD "none"
154 +#define CREATE_PREFIX "create:"
155 +#define BIND_PREFIX "bind:"
156 +#define BIND_AUDIO_GROUP_PREFIX BIND_PREFIX "AudioGroup:"
158 PA_MODULE_AUTHOR("Ismo Puustinen");
159 PA_MODULE_DESCRIPTION("Create audio groups and classify streams to them");
160 PA_MODULE_VERSION(PACKAGE_VERSION);
161 PA_MODULE_LOAD_ONCE(true);
163 -#ifndef AUDIO_GROUP_CONFIG
164 -#define AUDIO_GROUP_CONFIG "audio-groups.conf"
167 enum match_direction {
168 match_direction_unknown = 0,
169 match_direction_input,
170 @@ -80,60 +83,103 @@ struct expression {
171 PA_LLIST_HEAD(struct conjunction, conjunctions);
174 -/* data gathered from settings */
176 + struct userdata *userdata;
177 + pa_audio_group *audio_group;
178 + struct control *volume_control;
179 + struct control *mute_control;
180 + char *own_volume_control_name;
181 + char *own_mute_control_name;
182 + struct group *volume_master;
183 + struct group *mute_master;
184 + char *volume_master_name;
185 + char *mute_master_name;
187 + pa_hashmap *volume_slaves; /* struct group -> struct group (hashmap-as-a-set) */
188 + pa_hashmap *mute_slaves; /* struct group -> struct group (hashmap-as-a-set) */
189 + pa_hashmap *volume_stream_rules; /* struct stream_rule -> struct stream_rule (hashmap-as-a-set) */
190 + pa_hashmap *mute_stream_rules; /* struct stream_rule -> struct stream_rule (hashmap-as-a-set) */
195 -enum control_action {
196 - CONTROL_ACTION_NONE,
197 - CONTROL_ACTION_CREATE,
198 - CONTROL_ACTION_BIND,
200 + CONTROL_TYPE_VOLUME,
204 -struct audio_group {
206 struct userdata *userdata;
209 - enum control_action volume_control_action;
210 - enum control_action mute_control_action;
211 - pa_binding_target_info *volume_control_target_info;
212 - pa_binding_target_info *mute_control_target_info;
213 + enum control_type type;
216 + pa_volume_control *volume_control;
217 + pa_mute_control *mute_control;
220 + /* Controls that are created for streams don't own their pa_volume_control
221 + * and pa_mute_control objects, because they're owned by the streams. */
224 - /* official audio group */
225 - pa_audio_group *group;
226 + /* If non-NULL, then this control mirrors the state of the master
227 + * control. If someone changes the master state, the state of this control
228 + * is also updated, and also if someone changes this control's state, the
229 + * change is applied also to the master. */
230 + struct control *master;
232 - struct audio_group_control *volume_control;
233 - struct audio_group_control *mute_control;
234 + /* struct control -> struct control (hashmap-as-a-set)
235 + * Contains the controls that have this control as their master. */
236 + pa_hashmap *slaves;
238 + /* Set to true when the master control's state has been copied to this
240 + bool synced_with_master;
247 +struct stream_rule {
248 struct userdata *userdata;
251 enum match_direction direction;
252 char *audio_group_name_for_volume;
253 char *audio_group_name_for_mute;
254 - pa_audio_group *audio_group_for_volume;
255 - pa_audio_group *audio_group_for_mute;
256 - pa_binding_target_info *volume_control_target_info;
257 - pa_binding_target_info *mute_control_target_info;
258 - struct expression *rule;
261 + struct group *group_for_volume;
262 + struct group *group_for_mute;
263 + struct expression *match_expression;
267 - pa_hashmap *audio_groups; /* name -> struct audio_group */
268 - pa_dynarray *streams; /* struct stream */
269 - pa_hook_slot *new_stream_volume;
270 - pa_hook_slot *new_stream_mute;
272 - pa_volume_api *api;
274 - /* The following fields are only used during initialization. */
275 - pa_hashmap *audio_group_names; /* name -> name (hashmap-as-a-set) */
276 - pa_hashmap *unused_audio_groups; /* name -> struct audio_group */
277 - pa_dynarray *stream_names;
278 - pa_hashmap *unused_streams; /* name -> struct stream */
279 + pa_volume_api *volume_api;
280 + pa_hashmap *groups; /* name -> struct group */
281 + pa_hashmap *stream_rules; /* name -> struct stream_rule */
282 + pa_dynarray *stream_rules_list; /* struct stream_rule */
284 + /* pas_stream -> struct stream_rule
285 + * When a stream matches with a rule, it's added here. */
286 + pa_hashmap *rules_by_stream;
288 + /* pas_stream -> struct control
289 + * Contains proxy controls for all relative volume controls of streams. */
290 + pa_hashmap *stream_volume_controls;
292 + /* pas_stream -> struct control
293 + * Contains proxy controls for all mute controls of streams. */
294 + pa_hashmap *stream_mute_controls;
296 + pa_hook_slot *stream_put_slot;
297 + pa_hook_slot *stream_unlink_slot;
298 + pa_hook_slot *volume_control_implementation_initialized_slot;
299 + pa_hook_slot *mute_control_implementation_initialized_slot;
300 + pa_hook_slot *volume_control_set_initial_volume_slot;
301 + pa_hook_slot *mute_control_set_initial_mute_slot;
302 + pa_hook_slot *volume_control_volume_changed_slot;
303 + pa_hook_slot *mute_control_mute_changed_slot;
304 + pa_hook_slot *volume_control_unlink_slot;
305 + pa_hook_slot *mute_control_unlink_slot;
307 + pa_dynarray *stream_rule_names; /* Only used during initialization. */
310 static const char* const valid_modargs[] = {
311 @@ -141,77 +187,408 @@ static const char* const valid_modargs[] = {
315 -static void audio_group_unlink(struct audio_group *group);
316 +static void control_free(struct control *control);
317 +static void control_set_master(struct control *control, struct control *master);
318 +static void control_add_slave(struct control *control, struct control *slave);
319 +static void control_remove_slave(struct control *control, struct control *slave);
321 -static void print_literal(struct literal *l);
322 -static void print_conjunction(struct conjunction *c);
323 -static void print_expression(struct expression *e);
324 -static void delete_expression(struct expression *e);
325 +static void group_free(struct group *group);
326 +static void group_set_master(struct group *group, enum control_type type, struct group *master);
327 +static int group_set_master_name(struct group *group, enum control_type type, const char *name);
328 +static void group_disable_control(struct group *group, enum control_type type);
329 +static void group_add_slave(struct group *group, enum control_type type, struct group *slave);
330 +static void group_remove_slave(struct group *group, enum control_type type, struct group *slave);
332 -static struct audio_group *audio_group_new(struct userdata *u, const char *name) {
333 - struct audio_group *group;
334 +static void stream_rule_set_group(struct stream_rule *rule, enum control_type type, struct group *group);
335 +static void stream_rule_set_group_name(struct stream_rule *rule, enum control_type type, const char *name);
339 +static bool literal_match(struct literal *literal, pas_stream *stream);
341 - group = pa_xnew0(struct audio_group, 1);
342 - group->userdata = u;
343 - group->id = pa_xstrdup(name);
344 - group->description = pa_xstrdup(name);
345 - group->volume_control_action = CONTROL_ACTION_NONE;
346 - group->mute_control_action = CONTROL_ACTION_NONE;
347 +static struct expression *expression_new(void);
348 +static void expression_free(struct expression *expression);
350 +static int volume_control_set_volume_cb(pa_volume_control *volume_control, const pa_bvolume *original_volume,
351 + const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
352 + struct control *control;
353 + struct control *slave;
356 + pa_assert(volume_control);
357 + pa_assert(original_volume);
358 + pa_assert(remapped_volume);
360 + control = volume_control->userdata;
362 + /* There are four cases that need to be considered:
364 + * 1) The master control is propagating the volume to this control. We need
365 + * to propagate the volume downstream.
367 + * 2) This control was just assigned a master control and the volume hasn't
368 + * yet been synchronized. In this case the volume that is now being set for
369 + * this control is the master control's volume. We need to propagate the
370 + * volume downstream.
372 + * 3) Someone set the volume directly for this control, and this control
373 + * has a master control. We need to propagate the volume upstream, and wait
374 + * for another call that will fall under the case 1.
376 + * 4) Someone set the volume directly for this control, and this control
377 + * doesn't have a master control. We need to propagate the volume
380 + * As we can see, the action is the same in cases 1, 2 and 4. */
383 + if (control->synced_with_master && !control->master->volume_control->set_volume_in_progress) {
384 + pa_volume_control_set_volume(control->master->volume_control, original_volume, set_volume, set_balance);
388 + /* Cases 1, 2 and 4. */
389 + PA_HASHMAP_FOREACH(slave, control->slaves, state)
390 + pa_volume_control_set_volume(slave->volume_control, original_volume, set_volume, set_balance);
396 -static int audio_group_put(struct audio_group *group) {
398 +static int mute_control_set_mute_cb(pa_mute_control *mute_control, bool mute) {
399 + struct control *control;
400 + struct control *slave;
403 + pa_assert(mute_control);
405 + control = mute_control->userdata;
407 + /* There are four cases that need to be considered:
409 + * 1) The master control is propagating the mute to this control. We need
410 + * to propagate the mute downstream.
412 + * 2) This control was just assigned a master control and the mute hasn't
413 + * yet been synchronized. In this case the mute that is now being set for
414 + * this control is the master control's mute. We need to propagate the mute
417 + * 3) Someone set the mute directly for this control, and this control has
418 + * a master control. We need to propagate the mute upstream, and wait for
419 + * another call that will fall under the case 1.
421 + * 4) Someone set the mute directly for this control, and this control
422 + * doesn't have a master control. We need to propagate the mute downstream.
424 + * As we can see, the action is the same in cases 1, 2 and 4. */
427 + if (control->synced_with_master && !control->master->mute_control->set_mute_in_progress) {
428 + pa_mute_control_set_mute(control->master->mute_control, mute);
432 + /* Cases 1, 2 and 4. */
433 + PA_HASHMAP_FOREACH(slave, control->slaves, state)
434 + pa_mute_control_set_mute(slave->mute_control, mute);
439 +static int control_new_for_group(struct group *group, enum control_type type, const char *name, bool persistent, struct control **_r) {
440 + struct control *control = NULL;
447 + control = pa_xnew0(struct control, 1);
448 + control->userdata = group->userdata;
449 + control->type = type;
450 + control->slaves = pa_hashmap_new(NULL, NULL);
453 + case CONTROL_TYPE_VOLUME:
455 + control->volume_control = pa_hashmap_get(control->userdata->volume_api->volume_controls, name);
457 + if (!control->volume_control) {
458 + r = pa_volume_control_new(control->userdata->volume_api, name, persistent, &control->volume_control);
463 - r = pa_audio_group_new(group->userdata->api, group->id, group->description, &group->group);
466 + pa_volume_control_set_convertible_to_dB(control->volume_control, true);
469 + r = pa_volume_control_acquire_for_audio_group(control->volume_control, group->audio_group,
470 + volume_control_set_volume_cb, control);
474 + control->acquired = true;
476 + control->volume_control->set_volume = volume_control_set_volume_cb;
477 + control->volume_control->userdata = control;
481 + case CONTROL_TYPE_MUTE:
483 + control->mute_control = pa_hashmap_get(control->userdata->volume_api->mute_controls, name);
485 + if (!control->mute_control) {
486 + r = pa_mute_control_new(control->userdata->volume_api, name, persistent, &control->mute_control);
492 + r = pa_mute_control_acquire_for_audio_group(control->mute_control, group->audio_group,
493 + mute_control_set_mute_cb, control);
497 + control->acquired = true;
499 + control->mute_control->set_mute = mute_control_set_mute_cb;
500 + control->mute_control->userdata = control;
505 + control->own_control = true;
512 + control_free(control);
517 +static struct control *control_new_for_stream(struct userdata *u, enum control_type type, pas_stream *stream) {
518 + struct control *control;
523 - switch (group->volume_control_action) {
524 - case CONTROL_ACTION_NONE:
525 + control = pa_xnew0(struct control, 1);
526 + control->userdata = u;
527 + control->type = type;
530 + case CONTROL_TYPE_VOLUME:
531 + control->volume_control = stream->relative_volume_control;
532 + pa_assert(control->volume_control);
535 + case CONTROL_TYPE_MUTE:
536 + control->mute_control = stream->mute_control;
537 + pa_assert(control->mute_control);
541 - case CONTROL_ACTION_CREATE:
542 - pa_audio_group_set_have_own_volume_control(group->group, true);
543 - pa_audio_group_set_volume_control(group->group, group->group->own_volume_control);
547 +static void control_put(struct control *control) {
548 + pa_assert(control);
550 + switch (control->type) {
551 + case CONTROL_TYPE_VOLUME:
552 + if (control->own_control && !control->volume_control->linked)
553 + pa_volume_control_put(control->volume_control);
556 - case CONTROL_ACTION_BIND:
557 - pa_audio_group_bind_volume_control(group->group, group->volume_control_target_info);
558 + case CONTROL_TYPE_MUTE:
559 + if (control->own_control && !control->mute_control->linked)
560 + pa_mute_control_put(control->mute_control);
565 +static void control_unlink(struct control *control) {
566 + pa_assert(control);
568 + if (control->unlinked)
571 + control->unlinked = true;
573 + if (control->slaves) {
574 + struct control *slave;
576 + while ((slave = pa_hashmap_first(control->slaves)))
577 + control_set_master(slave, NULL);
580 + control_set_master(control, NULL);
582 - switch (group->mute_control_action) {
583 - case CONTROL_ACTION_NONE:
584 + switch (control->type) {
585 + case CONTROL_TYPE_VOLUME:
586 + if (control->own_control && control->volume_control && !control->volume_control->persistent)
587 + pa_volume_control_unlink(control->volume_control);
590 - case CONTROL_ACTION_CREATE:
591 - pa_audio_group_set_have_own_mute_control(group->group, true);
592 - pa_audio_group_set_mute_control(group->group, group->group->own_mute_control);
593 + case CONTROL_TYPE_MUTE:
594 + if (control->own_control && control->mute_control && !control->mute_control->persistent)
595 + pa_mute_control_unlink(control->mute_control);
600 +static void control_free(struct control *control) {
601 + pa_assert(control);
603 + if (!control->unlinked)
604 + control_unlink(control);
606 + if (control->slaves) {
607 + pa_assert(pa_hashmap_isempty(control->slaves));
608 + pa_hashmap_free(control->slaves);
611 + switch (control->type) {
612 + case CONTROL_TYPE_VOLUME:
613 + if (control->acquired)
614 + pa_volume_control_release(control->volume_control);
616 + if (control->own_control && control->volume_control && !control->volume_control->persistent)
617 + pa_volume_control_free(control->volume_control);
620 + case CONTROL_TYPE_MUTE:
621 + if (control->acquired)
622 + pa_mute_control_release(control->mute_control);
624 - case CONTROL_ACTION_BIND:
625 - pa_audio_group_bind_mute_control(group->group, group->mute_control_target_info);
626 + if (control->own_control && control->mute_control && !control->mute_control->persistent)
627 + pa_mute_control_free(control->mute_control);
631 - pa_audio_group_put(group->group);
635 +static void control_set_master(struct control *control, struct control *master) {
636 + struct control *old_master;
638 + pa_assert(control);
639 + pa_assert(!master || master->type == control->type);
641 + old_master = control->master;
643 + if (master == old_master)
647 + control_remove_slave(old_master, control);
648 + control->synced_with_master = false;
651 + control->master = master;
654 + control_add_slave(master, control);
656 + switch (control->type) {
657 + case CONTROL_TYPE_VOLUME:
658 + pa_volume_control_set_volume(control->volume_control, &master->volume_control->volume, true, true);
661 + case CONTROL_TYPE_MUTE:
662 + pa_mute_control_set_mute(control->mute_control, master->mute_control->mute);
666 + control->synced_with_master = true;
670 +static void control_add_slave(struct control *control, struct control *slave) {
671 + pa_assert(control);
674 + pa_assert_se(pa_hashmap_put(control->slaves, slave, slave) >= 0);
677 +static void control_remove_slave(struct control *control, struct control *slave) {
678 + pa_assert(control);
681 + pa_assert_se(pa_hashmap_remove(control->slaves, slave));
684 +static int group_new(struct userdata *u, const char *name, struct group **_r) {
685 + struct group *group = NULL;
687 + struct group *slave;
688 + struct stream_rule *rule;
695 + group = pa_xnew0(struct group, 1);
696 + group->userdata = u;
698 + r = pa_audio_group_new(u->volume_api, name, &group->audio_group);
702 + group->volume_slaves = pa_hashmap_new(NULL, NULL);
703 + group->mute_slaves = pa_hashmap_new(NULL, NULL);
704 + group->volume_stream_rules = pa_hashmap_new(NULL, NULL);
705 + group->mute_stream_rules = pa_hashmap_new(NULL, NULL);
707 + PA_HASHMAP_FOREACH(slave, u->groups, state) {
708 + if (slave == group)
711 + if (pa_safe_streq(slave->volume_master_name, group->audio_group->name))
712 + group_set_master(slave, CONTROL_TYPE_VOLUME, group);
714 + if (pa_safe_streq(slave->mute_master_name, group->audio_group->name))
715 + group_set_master(slave, CONTROL_TYPE_MUTE, group);
718 + PA_HASHMAP_FOREACH(rule, u->stream_rules, state) {
719 + if (pa_safe_streq(rule->audio_group_name_for_volume, group->audio_group->name))
720 + stream_rule_set_group(rule, CONTROL_TYPE_VOLUME, group);
722 + if (pa_safe_streq(rule->audio_group_name_for_mute, group->audio_group->name))
723 + stream_rule_set_group(rule, CONTROL_TYPE_MUTE, group);
730 - audio_group_unlink(group);
737 -static void audio_group_unlink(struct audio_group *group) {
738 +static void group_put(struct group *group) {
741 + pa_audio_group_put(group->audio_group);
743 + if (group->volume_control)
744 + control_put(group->volume_control);
746 + if (group->mute_control)
747 + control_put(group->mute_control);
750 +static void group_unlink(struct group *group) {
751 + struct stream_rule *rule;
752 + struct group *slave;
758 @@ -219,192 +596,406 @@ static void audio_group_unlink(struct audio_group *group) {
760 group->unlinked = true;
762 - if (group->group) {
763 - pa_audio_group_free(group->group);
764 - group->group = NULL;
766 + PA_HASHMAP_FOREACH(rule, group->volume_stream_rules, state)
767 + stream_rule_set_group(rule, CONTROL_TYPE_VOLUME, NULL);
769 + PA_HASHMAP_FOREACH(rule, group->mute_stream_rules, state)
770 + stream_rule_set_group(rule, CONTROL_TYPE_MUTE, NULL);
772 + PA_HASHMAP_FOREACH(slave, group->volume_slaves, state)
773 + group_set_master(slave, CONTROL_TYPE_VOLUME, NULL);
775 + PA_HASHMAP_FOREACH(slave, group->mute_slaves, state)
776 + group_set_master(slave, CONTROL_TYPE_MUTE, NULL);
778 + group_disable_control(group, CONTROL_TYPE_MUTE);
779 + group_disable_control(group, CONTROL_TYPE_VOLUME);
781 + if (group->audio_group)
782 + pa_audio_group_unlink(group->audio_group);
785 -static void audio_group_free(struct audio_group *group) {
786 +static void group_free(struct group *group) {
789 - if (!group->unlinked)
790 - audio_group_unlink(group);
791 + group_unlink(group);
793 + if (group->mute_stream_rules) {
794 + pa_assert(pa_hashmap_isempty(group->mute_stream_rules));
795 + pa_hashmap_free(group->mute_stream_rules);
798 + if (group->volume_stream_rules) {
799 + pa_assert(pa_hashmap_isempty(group->volume_stream_rules));
800 + pa_hashmap_free(group->volume_stream_rules);
803 + if (group->mute_slaves) {
804 + pa_assert(pa_hashmap_isempty(group->mute_slaves));
805 + pa_hashmap_free(group->mute_slaves);
808 + if (group->volume_slaves) {
809 + pa_assert(pa_hashmap_isempty(group->volume_slaves));
810 + pa_hashmap_free(group->volume_slaves);
813 - if (group->mute_control_target_info)
814 - pa_binding_target_info_free(group->mute_control_target_info);
815 + pa_assert(!group->mute_master_name);
816 + pa_assert(!group->volume_master_name);
817 + pa_assert(!group->mute_master);
818 + pa_assert(!group->volume_master);
819 + pa_assert(!group->mute_control);
820 + pa_assert(!group->volume_control);
822 - if (group->volume_control_target_info)
823 - pa_binding_target_info_free(group->volume_control_target_info);
824 + if (group->audio_group)
825 + pa_audio_group_free(group->audio_group);
827 - pa_xfree(group->description);
828 - pa_xfree(group->id);
832 -static void audio_group_set_description(struct audio_group *group, const char *description) {
833 +static void group_set_own_control_name(struct group *group, enum control_type type, const char *name) {
834 + struct group *slave;
838 - pa_assert(description);
840 - pa_xfree(group->description);
841 - group->description = pa_xstrdup(description);
843 + group_set_master_name(group, type, NULL);
846 + case CONTROL_TYPE_VOLUME:
847 + if (pa_safe_streq(name, group->own_volume_control_name))
850 + if (group->volume_control) {
851 + control_free(group->volume_control);
852 + group->volume_control = NULL;
855 + pa_xfree(group->own_volume_control_name);
856 + group->own_volume_control_name = pa_xstrdup(name);
859 + control_new_for_group(group, CONTROL_TYPE_VOLUME, name, true, &group->volume_control);
861 + PA_HASHMAP_FOREACH(slave, group->volume_slaves, state) {
862 + if (slave->volume_control)
863 + control_set_master(slave->volume_control, group->volume_control);
868 + case CONTROL_TYPE_MUTE:
869 + if (pa_safe_streq(name, group->own_mute_control_name))
872 + if (group->mute_control) {
873 + control_free(group->mute_control);
874 + group->mute_control = NULL;
877 + pa_xfree(group->own_mute_control_name);
878 + group->own_mute_control_name = pa_xstrdup(name);
881 + control_new_for_group(group, CONTROL_TYPE_MUTE, name, true, &group->mute_control);
883 + PA_HASHMAP_FOREACH(slave, group->mute_slaves, state) {
884 + if (slave->mute_control)
885 + control_set_master(slave->mute_control, group->mute_control);
892 -static void audio_group_set_volume_control_action(struct audio_group *group, enum control_action action,
893 - pa_binding_target_info *target_info) {
894 +static void group_set_master(struct group *group, enum control_type type, struct group *master) {
895 + struct group *old_master;
896 + struct control *master_control = NULL;
899 - pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
900 + pa_assert(master != group);
903 + case CONTROL_TYPE_VOLUME:
904 + old_master = group->volume_master;
906 + if (master == old_master)
910 + group_remove_slave(old_master, CONTROL_TYPE_VOLUME, group);
912 + group->volume_master = master;
915 + group_add_slave(master, CONTROL_TYPE_VOLUME, group);
917 + if (group->volume_control) {
919 + master_control = master->volume_control;
921 - group->volume_control_action = action;
922 + control_set_master(group->volume_control, master_control);
926 + case CONTROL_TYPE_MUTE:
927 + old_master = group->mute_master;
929 + if (master == old_master)
932 - if (group->volume_control_target_info)
933 - pa_binding_target_info_free(group->volume_control_target_info);
935 + group_remove_slave(old_master, CONTROL_TYPE_MUTE, group);
937 - if (action == CONTROL_ACTION_BIND)
938 - group->volume_control_target_info = pa_binding_target_info_copy(target_info);
940 - group->volume_control_target_info = NULL;
941 + group->mute_master = master;
944 + group_add_slave(master, CONTROL_TYPE_MUTE, group);
946 + if (group->mute_control) {
948 + master_control = master->volume_control;
950 + control_set_master(group->volume_control, master_control);
956 -static void audio_group_set_mute_control_action(struct audio_group *group, enum control_action action,
957 - pa_binding_target_info *target_info) {
958 +static int group_set_master_name(struct group *group, enum control_type type, const char *name) {
959 + struct group *slave;
961 + struct group *master = NULL;
964 - pa_assert((action == CONTROL_ACTION_BIND) ^ !target_info);
966 - group->mute_control_action = action;
967 + if (pa_safe_streq(name, group->audio_group->name)) {
968 + pa_log("Can't bind audio group control to itself.");
969 + return -PA_ERR_INVALID;
972 - if (group->mute_control_target_info)
973 - pa_binding_target_info_free(group->mute_control_target_info);
975 + group_set_own_control_name(group, type, NULL);
977 - if (action == CONTROL_ACTION_BIND)
978 - group->mute_control_target_info = pa_binding_target_info_copy(target_info);
980 - group->mute_control_target_info = NULL;
983 + case CONTROL_TYPE_VOLUME:
984 + if (pa_safe_streq(name, group->volume_master_name))
987 -static struct stream *stream_new(struct userdata *u, const char *name) {
988 - struct stream *stream;
989 + pa_xfree(group->volume_master_name);
990 + group->volume_master_name = pa_xstrdup(name);
994 + if (name && !group->volume_control) {
995 + control_new_for_group(group, CONTROL_TYPE_VOLUME, "audio-group-volume-control", false, &group->volume_control);
997 - stream = pa_xnew0(struct stream, 1);
998 - stream->userdata = u;
999 - stream->id = pa_xstrdup(name);
1000 - stream->direction = match_direction_unknown;
1001 + PA_HASHMAP_FOREACH(slave, group->volume_slaves, state) {
1002 + if (slave->volume_control)
1003 + control_set_master(slave->volume_control, group->volume_control);
1008 + } else if (!name && group->volume_control) {
1009 + control_free(group->volume_control);
1010 + group->volume_control = NULL;
1014 -static void stream_put(struct stream *stream) {
1015 - pa_assert(stream);
1016 + case CONTROL_TYPE_MUTE:
1017 + if (pa_safe_streq(name, group->mute_master_name))
1020 - if (stream->audio_group_name_for_volume) {
1021 - stream->audio_group_for_volume = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_volume);
1022 - if (stream->audio_group_for_volume)
1023 - stream->volume_control_target_info =
1024 - pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_volume->name,
1025 - PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL);
1027 - pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
1028 + pa_xfree(group->mute_master_name);
1029 + group->mute_master_name = pa_xstrdup(name);
1031 + if (name && !group->mute_control) {
1032 + control_new_for_group(group, CONTROL_TYPE_MUTE, "audio-group-mute-control", false, &group->mute_control);
1034 + PA_HASHMAP_FOREACH(slave, group->mute_slaves, state) {
1035 + if (slave->mute_control)
1036 + control_set_master(slave->mute_control, group->mute_control);
1039 + } else if (!name && group->mute_control) {
1040 + control_free(group->mute_control);
1041 + group->mute_control = NULL;
1046 - if (stream->audio_group_name_for_mute) {
1047 - stream->audio_group_for_mute = pa_hashmap_get(stream->userdata->audio_groups, stream->audio_group_name_for_mute);
1048 - if (stream->audio_group_for_mute)
1049 - stream->mute_control_target_info =
1050 - pa_binding_target_info_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, stream->audio_group_for_mute->name,
1051 - PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL);
1053 - pa_log("Stream %s refers to undefined audio group %s.", stream->id, stream->audio_group_name_for_volume);
1055 + master = pa_hashmap_get(group->userdata->groups, name);
1057 + group_set_master(group, type, master);
1062 +static void group_disable_control(struct group *group, enum control_type type) {
1065 + group_set_own_control_name(group, type, NULL);
1066 + group_set_master_name(group, type, NULL);
1069 +static void group_add_slave(struct group *group, enum control_type type, struct group *slave) {
1074 + case CONTROL_TYPE_VOLUME:
1075 + pa_assert_se(pa_hashmap_put(group->volume_slaves, slave, slave) >= 0);
1078 + case CONTROL_TYPE_MUTE:
1079 + pa_assert_se(pa_hashmap_put(group->mute_slaves, slave, slave) >= 0);
1084 -static void stream_unlink(struct stream *stream) {
1085 - pa_assert(stream);
1086 +static void group_remove_slave(struct group *group, enum control_type type, struct group *slave) {
1090 - if (stream->unlinked)
1093 + case CONTROL_TYPE_VOLUME:
1094 + pa_assert_se(pa_hashmap_remove(group->volume_slaves, slave));
1097 - if (stream->mute_control_target_info) {
1098 - pa_binding_target_info_free(stream->mute_control_target_info);
1099 - stream->mute_control_target_info = NULL;
1100 + case CONTROL_TYPE_MUTE:
1101 + pa_assert_se(pa_hashmap_remove(group->mute_slaves, slave));
1105 - if (stream->volume_control_target_info) {
1106 - pa_binding_target_info_free(stream->volume_control_target_info);
1107 - stream->volume_control_target_info = NULL;
1108 +static void group_add_stream_rule(struct group *group, enum control_type type, struct stream_rule *rule) {
1113 + case CONTROL_TYPE_VOLUME:
1114 + pa_assert_se(pa_hashmap_put(group->volume_stream_rules, rule, rule) >= 0);
1117 + case CONTROL_TYPE_MUTE:
1118 + pa_assert_se(pa_hashmap_put(group->mute_stream_rules, rule, rule) >= 0);
1123 +static void group_remove_stream_rule(struct group *group, enum control_type type, struct stream_rule *rule) {
1127 - stream->unlinked = true;
1129 + case CONTROL_TYPE_VOLUME:
1130 + pa_assert_se(pa_hashmap_remove(group->volume_stream_rules, rule));
1133 + case CONTROL_TYPE_MUTE:
1134 + pa_assert_se(pa_hashmap_remove(group->mute_stream_rules, rule));
1139 -static void stream_free(struct stream *stream) {
1140 - pa_assert(stream);
1141 +static struct stream_rule *stream_rule_new(struct userdata *u, const char *name) {
1142 + struct stream_rule *rule;
1144 - if (!stream->unlinked)
1145 - stream_unlink(stream);
1150 - delete_expression(stream->rule);
1151 + rule = pa_xnew0(struct stream_rule, 1);
1152 + rule->userdata = u;
1153 + rule->name = pa_xstrdup(name);
1154 + rule->direction = match_direction_unknown;
1155 + rule->match_expression = expression_new();
1157 - pa_xfree(stream->audio_group_name_for_mute);
1158 - pa_xfree(stream->audio_group_name_for_volume);
1159 - pa_xfree(stream->id);
1164 -static void stream_set_audio_group_name_for_volume(struct stream *stream, const char *name) {
1165 - pa_assert(stream);
1166 +static void stream_rule_free(struct stream_rule *rule) {
1169 - pa_xfree(stream->audio_group_name_for_volume);
1170 - stream->audio_group_name_for_volume = pa_xstrdup(name);
1171 + if (rule->match_expression)
1172 + expression_free(rule->match_expression);
1174 + stream_rule_set_group_name(rule, CONTROL_TYPE_MUTE, NULL);
1175 + stream_rule_set_group_name(rule, CONTROL_TYPE_VOLUME, NULL);
1176 + pa_xfree(rule->name);
1180 -static void stream_set_audio_group_name_for_mute(struct stream *stream, const char *name) {
1181 - pa_assert(stream);
1182 +static void stream_rule_set_match_expression(struct stream_rule *rule, struct expression *expression) {
1184 + pa_assert(expression);
1186 - pa_xfree(stream->audio_group_name_for_mute);
1187 - stream->audio_group_name_for_mute = pa_xstrdup(name);
1188 + if (rule->match_expression)
1189 + expression_free(rule->match_expression);
1191 + rule->match_expression = expression;
1194 -/* stream classification */
1195 +static void stream_rule_set_group(struct stream_rule *rule, enum control_type type, struct group *group) {
1198 -static bool match_predicate(struct literal *l, pas_stream *d) {
1200 + case CONTROL_TYPE_VOLUME:
1201 + if (group == rule->group_for_volume)
1204 - if (l->stream_direction != match_direction_unknown) {
1205 - /* check the stream direction; _sink inputs_ are always _outputs_ */
1206 + if (rule->group_for_volume)
1207 + group_remove_stream_rule(rule->group_for_volume, CONTROL_TYPE_VOLUME, rule);
1209 - if ((d->direction == PA_DIRECTION_OUTPUT && l->stream_direction == match_direction_output) ||
1210 - ((d->direction == PA_DIRECTION_INPUT && l->stream_direction == match_direction_input))) {
1213 + rule->group_for_volume = group;
1216 + group_add_stream_rule(group, CONTROL_TYPE_VOLUME, rule);
1219 + case CONTROL_TYPE_MUTE:
1220 + if (group == rule->group_for_mute)
1223 + if (rule->group_for_mute)
1224 + group_remove_stream_rule(rule->group_for_mute, CONTROL_TYPE_MUTE, rule);
1226 + rule->group_for_mute = group;
1229 + group_add_stream_rule(group, CONTROL_TYPE_MUTE, rule);
1232 - else if (l->property_name && l->property_value) {
1233 - /* check the property from the property list */
1236 - if (pa_proplist_contains(d->proplist, l->property_name)) {
1237 - const char *prop = pa_proplist_gets(d->proplist, l->property_name);
1238 +static void stream_rule_set_group_name(struct stream_rule *rule, enum control_type type, const char *name) {
1239 + struct group *group = NULL;
1241 - if (prop && strcmp(prop, l->property_value) == 0) {
1248 + case CONTROL_TYPE_VOLUME:
1249 + pa_xfree(rule->audio_group_name_for_volume);
1250 + rule->audio_group_name_for_volume = pa_xstrdup(name);
1253 + case CONTROL_TYPE_MUTE:
1254 + pa_xfree(rule->audio_group_name_for_mute);
1255 + rule->audio_group_name_for_mute = pa_xstrdup(name);
1263 + group = pa_hashmap_get(rule->userdata->groups, name);
1265 -static bool match_rule(struct expression *e, pas_stream *d) {
1266 + stream_rule_set_group(rule, type, group);
1269 +static bool stream_rule_match(struct stream_rule *rule, pas_stream *stream) {
1270 struct conjunction *c;
1272 - PA_LLIST_FOREACH(c, e->conjunctions) {
1273 + PA_LLIST_FOREACH(c, rule->match_expression->conjunctions) {
1275 bool and_success = true;
1276 PA_LLIST_FOREACH(l, c->literals) {
1277 - if (!match_predicate(l, d)) {
1278 + if (!literal_match(l, stream)) {
1279 /* at least one fail for conjunction */
1280 and_success = false;
1282 @@ -421,56 +1012,246 @@ static bool match_rule(struct expression *e, pas_stream *d) {
1286 -static void classify_stream(struct userdata *u, pas_stream *new_data, bool mute) {
1287 - /* do the classification here */
1288 +/* stream classification */
1290 - struct stream *stream = NULL;
1292 +static bool literal_match(struct literal *literal, pas_stream *stream) {
1294 + if (literal->stream_direction != match_direction_unknown) {
1295 + /* check the stream direction; _sink inputs_ are always _outputs_ */
1297 - /* go through the stream match definitions in given order */
1298 + if ((stream->direction == PA_DIRECTION_OUTPUT && literal->stream_direction == match_direction_output) ||
1299 + (stream->direction == PA_DIRECTION_INPUT && literal->stream_direction == match_direction_input)) {
1300 + return literal->negation ? false : true;
1303 + else if (literal->property_name && literal->property_value) {
1304 + /* check the property from the property list */
1306 - PA_DYNARRAY_FOREACH(stream, u->streams, idx) {
1307 - if (stream->rule && match_rule(stream->rule, new_data)) {
1308 - pa_log_info("stream %s (%s) match with rule %s:", new_data->name, new_data->description, stream->id);
1309 - print_expression(stream->rule);
1310 + if (pa_proplist_contains(stream->proplist, literal->property_name)) {
1311 + const char *prop = pa_proplist_gets(stream->proplist, literal->property_name);
1314 - if (new_data->use_default_mute_control && stream->audio_group_for_mute)
1315 - pas_stream_bind_mute_control(new_data, stream->mute_control_target_info);
1317 - if (new_data->use_default_volume_control && stream->audio_group_for_volume)
1318 - pas_stream_bind_volume_control(new_data, stream->volume_control_target_info);
1320 + if (prop && strcmp(prop, literal->property_value) == 0)
1321 + return literal->negation ? false : true;
1326 + return literal->negation ? true : false;
1329 +static pa_hook_result_t stream_put_cb(void *hook_data, void *call_data, void *userdata) {
1330 + struct userdata *u = userdata;
1331 + pas_stream *stream = call_data;
1332 + struct stream_rule *rule;
1337 + pa_assert(stream);
1339 + PA_DYNARRAY_FOREACH(rule, u->stream_rules_list, idx) {
1340 + if (stream_rule_match(rule, stream)) {
1341 + pa_hashmap_put(u->rules_by_stream, stream, rule);
1346 - /* no matches, don't touch the volumes */
1347 + return PA_HOOK_OK;
1350 +static pa_hook_result_t stream_unlink_cb(void *hook_data, void *call_data, void *userdata) {
1351 + struct userdata *u = userdata;
1352 + pas_stream *stream = call_data;
1355 + pa_assert(stream);
1357 + pa_hashmap_remove(u->rules_by_stream, stream);
1359 + return PA_HOOK_OK;
1362 +static pa_hook_result_t volume_control_implementation_initialized_cb(void *hook_data, void *call_data, void *userdata) {
1363 + struct userdata *u = userdata;
1364 + pa_volume_control *volume_control = call_data;
1365 + struct control *control;
1368 + pa_assert(volume_control);
1370 + if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1371 + return PA_HOOK_OK;
1373 + control = control_new_for_stream(u, CONTROL_TYPE_VOLUME, volume_control->owner_stream);
1374 + control_put(control);
1375 + pa_assert_se(pa_hashmap_put(u->stream_volume_controls, volume_control->owner_stream, control) >= 0);
1377 + return PA_HOOK_OK;
1380 +static pa_hook_result_t mute_control_implementation_initialized_cb(void *hook_data, void *call_data, void *userdata) {
1381 + struct userdata *u = userdata;
1382 + pa_mute_control *mute_control = call_data;
1383 + struct control *control;
1386 + pa_assert(mute_control);
1388 + if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1389 + return PA_HOOK_OK;
1391 + control = control_new_for_stream(u, CONTROL_TYPE_MUTE, mute_control->owner_stream);
1392 + control_put(control);
1393 + pa_assert_se(pa_hashmap_put(u->stream_mute_controls, mute_control->owner_stream, control) >= 0);
1395 + return PA_HOOK_OK;
1398 +static pa_hook_result_t volume_control_set_initial_volume_cb(void *hook_data, void *call_data, void *userdata) {
1399 + struct userdata *u = userdata;
1400 + pa_volume_control *volume_control = call_data;
1401 + struct stream_rule *rule;
1402 + struct control *control;
1405 + pa_assert(volume_control);
1407 + if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1408 + return PA_HOOK_OK;
1410 + rule = pa_hashmap_get(u->rules_by_stream, volume_control->owner_stream);
1412 + return PA_HOOK_OK;
1414 + if (!rule->group_for_volume)
1415 + return PA_HOOK_OK;
1417 + if (!rule->group_for_volume->volume_control)
1418 + return PA_HOOK_OK;
1420 + control = pa_hashmap_get(u->stream_volume_controls, volume_control->owner_stream);
1421 + pa_assert(control);
1422 + pa_assert(control->volume_control == volume_control);
1424 + /* This will set the volume for volume_control. */
1425 + control_set_master(control, rule->group_for_volume->volume_control);
1427 + return PA_HOOK_STOP;
1430 +static pa_hook_result_t mute_control_set_initial_mute_cb(void *hook_data, void *call_data, void *userdata) {
1431 + struct userdata *u = userdata;
1432 + pa_mute_control *mute_control = call_data;
1433 + struct stream_rule *rule;
1434 + struct control *control;
1437 + pa_assert(mute_control);
1439 + if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1440 + return PA_HOOK_OK;
1442 + rule = pa_hashmap_get(u->rules_by_stream, mute_control->owner_stream);
1444 + return PA_HOOK_OK;
1446 + if (!rule->group_for_mute)
1447 + return PA_HOOK_OK;
1449 + if (!rule->group_for_mute->mute_control)
1450 + return PA_HOOK_OK;
1452 + control = pa_hashmap_get(u->stream_mute_controls, mute_control->owner_stream);
1453 + pa_assert(control);
1454 + pa_assert(control->mute_control == mute_control);
1456 + /* This will set the mute for mute_control. */
1457 + control_set_master(control, rule->group_for_mute->mute_control);
1459 + return PA_HOOK_STOP;
1462 +static pa_hook_result_t volume_control_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
1463 + struct userdata *u = userdata;
1464 + pa_volume_control *volume_control = call_data;
1465 + struct control *control;
1468 + pa_assert(volume_control);
1470 + if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1471 + return PA_HOOK_OK;
1473 + control = pa_hashmap_get(u->stream_volume_controls, volume_control->owner_stream);
1475 + return PA_HOOK_OK;
1477 + if (!control->master)
1478 + return PA_HOOK_OK;
1480 + pa_volume_control_set_volume(control->master->volume_control, &volume_control->volume, true, true);
1482 + return PA_HOOK_OK;
1485 +static pa_hook_result_t mute_control_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
1486 + struct userdata *u = userdata;
1487 + pa_mute_control *mute_control = call_data;
1488 + struct control *control;
1491 + pa_assert(mute_control);
1493 + if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1494 + return PA_HOOK_OK;
1496 + control = pa_hashmap_get(u->stream_mute_controls, mute_control->owner_stream);
1498 + return PA_HOOK_OK;
1500 + if (!control->master)
1501 + return PA_HOOK_OK;
1503 + pa_mute_control_set_mute(control->master->mute_control, mute_control->mute);
1505 + return PA_HOOK_OK;
1508 -static pa_hook_result_t set_volume_control_cb(
1510 - pas_stream *new_data,
1511 - struct userdata *u) {
1512 +static pa_hook_result_t volume_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
1513 + struct userdata *u = userdata;
1514 + pa_volume_control *volume_control = call_data;
1515 + struct control *control;
1517 - pa_assert(new_data);
1519 + pa_assert(volume_control);
1521 + if (volume_control->purpose != PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME)
1522 + return PA_HOOK_OK;
1524 - classify_stream(u, new_data, false);
1525 + control = pa_hashmap_remove(u->stream_volume_controls, volume_control->owner_stream);
1527 + return PA_HOOK_OK;
1529 + control_free(control);
1534 -static pa_hook_result_t set_mute_control_cb(
1536 - pas_stream *new_data,
1537 - struct userdata *u) {
1538 +static pa_hook_result_t mute_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
1539 + struct userdata *u = userdata;
1540 + pa_mute_control *mute_control = call_data;
1541 + struct control *control;
1543 - pa_assert(new_data);
1545 + pa_assert(mute_control);
1547 + if (mute_control->purpose != PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE)
1548 + return PA_HOOK_OK;
1550 - classify_stream(u, new_data, true);
1551 + control = pa_hashmap_remove(u->stream_mute_controls, mute_control->owner_stream);
1553 + return PA_HOOK_OK;
1555 + control_free(control);
1559 @@ -520,6 +1301,7 @@ static pa_hook_result_t set_mute_control_cb(
1560 (property application.process.binary=paplay OR (direction input OR direction output))
1564 static void print_literal(struct literal *l) {
1565 if (l->stream_direction != match_direction_unknown) {
1566 pa_log_info(" %sstream direction %s",
1567 @@ -549,6 +1331,7 @@ static void print_expression(struct expression *e) {
1568 print_conjunction(c);
1573 static void delete_literal(struct literal *l) {
1575 @@ -573,14 +1356,23 @@ static void delete_conjunction(struct conjunction *c) {
1579 -static void delete_expression(struct expression *e) {
1580 +static struct expression *expression_new(void) {
1581 + struct expression *expression;
1583 + expression = pa_xnew0(struct expression, 1);
1585 + return expression;
1588 +static void expression_free(struct expression *expression) {
1589 struct conjunction *c;
1591 - PA_LLIST_FOREACH(c, e->conjunctions) {
1592 + pa_assert(expression);
1594 + PA_LLIST_FOREACH(c, expression->conjunctions)
1595 delete_conjunction(c);
1599 + pa_xfree(expression);
1602 enum logic_operator {
1603 @@ -917,26 +1709,21 @@ static bool gather_expression(struct expression *e, struct expression_token *et)
1607 -static struct expression *parse_rule(const char *rule_string) {
1609 +static int expression_from_string(const char *str, struct expression **_r) {
1612 struct expression *e = NULL;
1615 struct expression_token *et = NULL;
1620 - len = strlen(rule_string);
1622 - buf = (char *) pa_xmalloc0(len);
1628 + buf = pa_xmalloc0(strlen(str) + 1);
1630 /* remove whitespace */
1632 - k = (char *) rule_string;
1637 @@ -958,9 +1745,6 @@ static struct expression *parse_rule(const char *rule_string) {
1639 e = pa_xnew0(struct expression, 1);
1644 PA_LLIST_HEAD_INIT(struct conjunction, e->conjunctions);
1646 /* gather expressions to actual match format */
1647 @@ -978,30 +1762,15 @@ static struct expression *parse_rule(const char *rule_string) {
1648 delete_expression_token(et);
1656 delete_expression_token(et);
1662 -static int parse_audio_groups(pa_config_parser_state *state) {
1663 - struct userdata *u;
1665 - const char *split_state = NULL;
1669 - u = state->userdata;
1671 - pa_hashmap_remove_all(u->audio_group_names);
1672 + expression_free(e);
1674 - while ((name = pa_split_spaces(state->rvalue, &split_state)))
1675 - pa_hashmap_put(u->audio_group_names, name, name);
1678 + return -PA_ERR_INVALID;
1681 static int parse_streams(pa_config_parser_state *state) {
1682 @@ -1013,15 +1782,13 @@ static int parse_streams(pa_config_parser_state *state) {
1684 u = state->userdata;
1686 - pa_dynarray_remove_all(u->stream_names);
1688 while ((name = pa_split_spaces(state->rvalue, &split_state))) {
1691 bool duplicate = false;
1693 - /* Avoid adding duplicates in u->stream_names. */
1694 - PA_DYNARRAY_FOREACH(name2, u->stream_names, idx) {
1695 + /* Avoid adding duplicates in u->stream_rule_names. */
1696 + PA_DYNARRAY_FOREACH(name2, u->stream_rule_names, idx) {
1697 if (pa_streq(name, name2)) {
1700 @@ -1033,230 +1800,221 @@ static int parse_streams(pa_config_parser_state *state) {
1704 - pa_dynarray_append(u->stream_names, name);
1705 + pa_dynarray_append(u->stream_rule_names, name);
1711 -static int parse_common(pa_config_parser_state *state) {
1712 -#define AUDIOGROUP_START "AudioGroup "
1713 -#define STREAM_START "Stream "
1714 -#define BIND_KEYWORD "bind:"
1715 -#define NONE_KEYWORD "none"
1718 - struct userdata *u = (struct userdata *) state->userdata;
1720 - pa_binding_target_info *target_info;
1722 +static int parse_group_control(pa_config_parser_state *state, struct group *group, enum control_type type) {
1726 - section = state->section;
1729 + if (pa_streq(state->rvalue, NONE_KEYWORD))
1730 + group_disable_control(group, type);
1732 + else if (pa_startswith(state->rvalue, CREATE_PREFIX))
1733 + group_set_own_control_name(group, type, state->rvalue + strlen(CREATE_PREFIX));
1735 - if (strncmp(section, AUDIOGROUP_START, strlen(AUDIOGROUP_START)) == 0) {
1736 - char *ag_name = section + strlen(AUDIOGROUP_START);
1737 - struct audio_group *ag = (struct audio_group *) pa_hashmap_get(u->unused_audio_groups, ag_name);
1738 + else if (pa_startswith(state->rvalue, BIND_PREFIX)) {
1739 + if (pa_startswith(state->rvalue, BIND_AUDIO_GROUP_PREFIX)) {
1743 - /* first item for this audio group section, so create the struct */
1744 - ag = audio_group_new(u, ag_name);
1745 - pa_hashmap_put(u->unused_audio_groups, ag->id, ag);
1746 + r = group_set_master_name(group, type, state->rvalue + strlen(BIND_AUDIO_GROUP_PREFIX));
1748 + pa_log("[%s:%u] Failed to set binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
1752 + pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
1753 + return -PA_ERR_INVALID;
1756 + pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1757 + return -PA_ERR_INVALID;
1760 - if (strcmp(state->lvalue, "description") == 0)
1761 - audio_group_set_description(ag, state->rvalue);
1765 +static int parse_common(pa_config_parser_state *state) {
1767 + struct userdata *u = state->userdata;
1771 - else if (strcmp(state->lvalue, "volume-control") == 0) {
1772 - if (pa_streq(state->rvalue, "create"))
1773 - audio_group_set_volume_control_action(ag, CONTROL_ACTION_CREATE, NULL);
1776 - else if (pa_streq(state->rvalue, NONE_KEYWORD))
1777 - audio_group_set_volume_control_action(ag, CONTROL_ACTION_NONE, NULL);
1778 + section = state->section;
1780 + pa_log("[%s:%u] Lvalue \"%s\" not expected in the General section.", state->filename, state->lineno, state->lvalue);
1781 + return -PA_ERR_INVALID;
1784 - else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
1785 - r = pa_binding_target_info_new_from_string(state->rvalue, "volume_control", &target_info);
1787 - pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
1790 + if (pa_startswith(section, AUDIOGROUP_START)) {
1791 + struct group *group;
1793 - audio_group_set_volume_control_action(ag, CONTROL_ACTION_BIND, target_info);
1794 - pa_binding_target_info_free(target_info);
1795 + name = section + strlen(AUDIOGROUP_START);
1798 - pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1800 + group = pa_hashmap_get(u->groups, name);
1802 + r = group_new(u, name, &group);
1804 + pa_log("[%s:%u] Failed to create an audio group with name \"%s\".", state->filename, state->lineno, name);
1808 + pa_hashmap_put(u->groups, (void *) group->audio_group->name, group);
1810 - else if (strcmp(state->lvalue, "mute-control") == 0) {
1811 - if (pa_streq(state->rvalue, "create"))
1812 - audio_group_set_mute_control_action(ag, CONTROL_ACTION_CREATE, NULL);
1814 - else if (pa_streq(state->rvalue, NONE_KEYWORD))
1815 - audio_group_set_mute_control_action(ag, CONTROL_ACTION_NONE, NULL);
1817 - else if (pa_startswith(state->rvalue, BIND_KEYWORD)) {
1818 - r = pa_binding_target_info_new_from_string(state->rvalue, "mute_control", &target_info);
1820 - pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
1824 - audio_group_set_mute_control_action(ag, CONTROL_ACTION_BIND, target_info);
1825 - pa_binding_target_info_free(target_info);
1826 + if (pa_streq(state->lvalue, "description"))
1827 + pa_audio_group_set_description(group->audio_group, state->rvalue);
1830 - pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1833 + else if (pa_streq(state->lvalue, "volume-control"))
1834 + return parse_group_control(state, group, CONTROL_TYPE_VOLUME);
1836 + else if (pa_streq(state->lvalue, "mute-control"))
1837 + return parse_group_control(state, group, CONTROL_TYPE_MUTE);
1840 + pa_log("[%s:%u] Lvalue \"%s\" not expected in the AudioGroup section.", state->filename, state->lineno, state->lvalue);
1841 + return -PA_ERR_INVALID;
1844 - else if (strncmp(section, STREAM_START, strlen(STREAM_START)) == 0) {
1845 - char *stream_name = section + strlen(STREAM_START);
1846 + else if (pa_startswith(section, STREAM_RULE_START)) {
1847 + struct stream_rule *rule;
1849 - struct stream *stream = (struct stream *) pa_hashmap_get(u->unused_streams, stream_name);
1850 + name = section + strlen(STREAM_RULE_START);
1853 - /* first item for this stream section, so create the struct */
1854 - stream = stream_new(u, stream_name);
1855 - pa_hashmap_put(u->unused_streams, stream->id, stream);
1856 + rule = pa_hashmap_get(u->stream_rules, name);
1858 + rule = stream_rule_new(u, name);
1859 + pa_hashmap_put(u->stream_rules, rule->name, rule);
1862 if (pa_streq(state->lvalue, "audio-group-for-volume"))
1863 - stream_set_audio_group_name_for_volume(stream, *state->rvalue ? state->rvalue : NULL);
1864 + stream_rule_set_group_name(rule, CONTROL_TYPE_VOLUME, state->rvalue);
1866 else if (pa_streq(state->lvalue, "audio-group-for-mute"))
1867 - stream_set_audio_group_name_for_mute(stream, *state->rvalue ? state->rvalue : NULL);
1868 + stream_rule_set_group_name(rule, CONTROL_TYPE_MUTE, state->rvalue);
1870 - else if (strcmp(state->lvalue, "match") == 0) {
1871 - if (!state->rvalue)
1873 + else if (pa_streq(state->lvalue, "match")) {
1874 + struct expression *expression;
1876 - stream->rule = parse_rule(state->rvalue);
1878 - if (!stream->rule) {
1880 + r = expression_from_string(state->rvalue, &expression);
1882 + pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
1886 + stream_rule_set_match_expression(rule, expression);
1894 - pa_log_error("failed parsing audio group definition file");
1897 -#undef NONE_KEYWORD
1898 -#undef AUDIO_GROUP_KEYWORD
1899 -#undef BIND_KEYWORD
1900 -#undef STREAM_START
1901 -#undef AUDIOGROUP_START
1904 -static void finalize_config(struct userdata *u) {
1905 - const char *group_name;
1906 +int pa__init(pa_module *module) {
1907 + pa_modargs *ma = NULL;
1908 + struct userdata *u;
1911 + struct group *group;
1913 - struct audio_group *group;
1914 - const char *stream_name;
1917 - struct stream *stream;
1921 - PA_HASHMAP_FOREACH(group_name, u->audio_group_names, state) {
1923 + pa_assert(module);
1925 - group = pa_hashmap_remove(u->unused_audio_groups, group_name);
1927 - group = audio_group_new(u, group_name);
1929 - r = audio_group_put(group);
1931 - pa_log("Failed to create audio group %s.", group_name);
1932 - audio_group_free(group);
1936 - pa_assert_se(pa_hashmap_put(u->audio_groups, group->id, group) >= 0);
1937 + if (!(ma = pa_modargs_new(module->argument, valid_modargs))) {
1938 + pa_log("Failed to parse module arguments");
1942 - PA_HASHMAP_FOREACH(group, u->unused_audio_groups, state)
1943 - pa_log_debug("Audio group %s is not used.", group->id);
1945 - pa_hashmap_free(u->unused_audio_groups);
1946 - u->unused_audio_groups = NULL;
1948 - pa_hashmap_free(u->audio_group_names);
1949 - u->audio_group_names = NULL;
1951 - PA_DYNARRAY_FOREACH(stream_name, u->stream_names, idx) {
1952 - stream = pa_hashmap_remove(u->unused_streams, stream_name);
1954 - pa_log("Reference to undefined stream %s, ignoring.", stream_name);
1957 + u = module->userdata = pa_xnew0(struct userdata, 1);
1958 + u->volume_api = pa_volume_api_get(module->core);
1959 + u->groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1960 + (pa_free_cb_t) group_free);
1961 + u->stream_rules = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1962 + (pa_free_cb_t) stream_rule_free);
1963 + u->stream_rules_list = pa_dynarray_new(NULL);
1964 + u->rules_by_stream = pa_hashmap_new(NULL, NULL);
1965 + u->stream_volume_controls = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) control_free);
1966 + u->stream_mute_controls = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) control_free);
1967 + u->stream_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PUT], PA_HOOK_NORMAL, stream_put_cb,
1969 + u->stream_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], PA_HOOK_NORMAL,
1970 + stream_unlink_cb, u);
1971 + u->volume_control_implementation_initialized_slot =
1972 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED],
1973 + PA_HOOK_NORMAL, volume_control_implementation_initialized_cb, u);
1974 + u->mute_control_implementation_initialized_slot =
1975 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED],
1976 + PA_HOOK_NORMAL, mute_control_implementation_initialized_cb, u);
1977 + u->volume_control_set_initial_volume_slot =
1978 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME], PA_HOOK_NORMAL,
1979 + volume_control_set_initial_volume_cb, u);
1980 + u->mute_control_set_initial_mute_slot =
1981 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE], PA_HOOK_NORMAL,
1982 + mute_control_set_initial_mute_cb, u);
1983 + u->volume_control_volume_changed_slot =
1984 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED], PA_HOOK_NORMAL,
1985 + volume_control_volume_changed_cb, u);
1986 + u->mute_control_mute_changed_slot =
1987 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], PA_HOOK_NORMAL,
1988 + mute_control_mute_changed_cb, u);
1989 + u->volume_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK],
1990 + PA_HOOK_NORMAL, volume_control_unlink_cb, u);
1991 + u->mute_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
1992 + PA_HOOK_NORMAL, mute_control_unlink_cb, u);
1993 + u->stream_rule_names = pa_dynarray_new(pa_xfree);
1995 + f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "audio-groups.conf", "audio-groups.conf", NULL, &fn);
1997 + pa_config_item config_items[] = {
1998 + { "stream-rules", parse_streams, NULL, "General" },
1999 + { NULL, parse_common, NULL, NULL },
2000 + { NULL, NULL, NULL, NULL },
2003 - stream_put(stream);
2004 - pa_dynarray_append(u->streams, stream);
2005 + pa_config_parse(fn, f, config_items, NULL, u);
2012 - PA_HASHMAP_FOREACH(stream, u->unused_streams, state)
2013 - pa_log_debug("Stream %s is not used.", stream->id);
2015 - pa_hashmap_free(u->unused_streams);
2016 - u->unused_streams = NULL;
2018 - pa_dynarray_free(u->stream_names);
2019 - u->stream_names = NULL;
2021 + PA_HASHMAP_FOREACH(group, u->groups, state)
2024 -static bool parse_configuration(struct userdata *u, const char *filename) {
2027 + PA_DYNARRAY_FOREACH(name, u->stream_rule_names, idx) {
2028 + struct stream_rule *rule;
2030 - pa_config_item table[] = {
2031 - { "audio-groups", parse_audio_groups, NULL, "General" },
2032 - { "streams", parse_streams, NULL, "General" },
2033 - { NULL, parse_common, NULL, NULL },
2034 - { NULL, NULL, NULL, NULL },
2036 + rule = pa_hashmap_get(u->stream_rules, name);
2038 + pa_dynarray_append(u->stream_rules_list, rule);
2040 + pa_log("Non-existent stream rule \"%s\" referenced, ignoring.", name);
2043 - u->audio_group_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
2044 - u->unused_audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2045 - (pa_free_cb_t) audio_group_free);
2046 - u->stream_names = pa_dynarray_new(pa_xfree);
2047 - u->unused_streams = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2048 - (pa_free_cb_t) stream_free);
2049 + pa_dynarray_free(u->stream_rule_names);
2050 + u->stream_rule_names = NULL;
2052 - if (pa_is_path_absolute(filename))
2053 - f = pa_open_config_file(filename, NULL, NULL, &fn);
2055 - char *sys_conf_file;
2056 + pa_modargs_free(ma);
2058 - sys_conf_file = pa_sprintf_malloc(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "%s", filename);
2059 - f = pa_open_config_file(sys_conf_file, filename, NULL, &fn);
2060 - pa_xfree(sys_conf_file);
2065 - pa_config_parse(fn, f, table, NULL, u);
2074 - finalize_config(u);
2076 + pa_modargs_free(ma);
2082 void pa__done(pa_module *m) {
2083 - struct userdata* u;
2084 + struct userdata *u;
2088 @@ -1265,70 +2023,56 @@ void pa__done(pa_module *m) {
2092 - if (u->new_stream_volume)
2093 - pa_hook_slot_free(u->new_stream_volume);
2094 + if (u->mute_control_unlink_slot)
2095 + pa_hook_slot_free(u->mute_control_unlink_slot);
2097 - if (u->new_stream_mute)
2098 - pa_hook_slot_free(u->new_stream_mute);
2099 + if (u->volume_control_unlink_slot)
2100 + pa_hook_slot_free(u->volume_control_unlink_slot);
2103 - pa_dynarray_free(u->streams);
2104 + if (u->mute_control_mute_changed_slot)
2105 + pa_hook_slot_free(u->mute_control_mute_changed_slot);
2107 - if (u->audio_groups)
2108 - pa_hashmap_free(u->audio_groups);
2109 + if (u->volume_control_volume_changed_slot)
2110 + pa_hook_slot_free(u->volume_control_volume_changed_slot);
2113 - pa_volume_api_unref(u->api);
2114 + if (u->mute_control_set_initial_mute_slot)
2115 + pa_hook_slot_free(u->mute_control_set_initial_mute_slot);
2119 + if (u->volume_control_set_initial_volume_slot)
2120 + pa_hook_slot_free(u->volume_control_set_initial_volume_slot);
2122 -int pa__init(pa_module *m) {
2123 - pa_modargs *ma = NULL;
2124 - struct userdata *u;
2125 - const char *filename;
2126 + if (u->mute_control_implementation_initialized_slot)
2127 + pa_hook_slot_free(u->mute_control_implementation_initialized_slot);
2130 + if (u->volume_control_implementation_initialized_slot)
2131 + pa_hook_slot_free(u->volume_control_implementation_initialized_slot);
2133 - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
2134 - pa_log("Failed to parse module arguments");
2137 + if (u->stream_unlink_slot)
2138 + pa_hook_slot_free(u->stream_unlink_slot);
2140 - u = m->userdata = pa_xnew0(struct userdata, 1);
2141 + if (u->stream_put_slot)
2142 + pa_hook_slot_free(u->stream_put_slot);
2146 + if (u->stream_mute_controls)
2147 + pa_hashmap_free(u->stream_mute_controls);
2149 - u->api = pa_volume_api_get(m->core);
2150 + if (u->stream_volume_controls)
2151 + pa_hashmap_free(u->stream_volume_controls);
2156 - u->audio_groups = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2157 - (pa_free_cb_t) audio_group_free);
2158 - u->streams = pa_dynarray_new((pa_free_cb_t) stream_free);
2160 - filename = pa_modargs_get_value(ma, "filename", AUDIO_GROUP_CONFIG);
2162 - if (!parse_configuration(u, filename))
2164 + if (u->rules_by_stream)
2165 + pa_hashmap_free(u->rules_by_stream);
2167 - u->new_stream_volume = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_volume_control_cb, u);
2168 - u->new_stream_mute = pa_hook_connect(&u->api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], PA_HOOK_EARLY, (pa_hook_cb_t) set_mute_control_cb, u);
2169 + if (u->stream_rules_list)
2170 + pa_dynarray_free(u->stream_rules_list);
2172 - if (!u->new_stream_volume || !u->new_stream_mute)
2174 + if (u->stream_rules)
2175 + pa_hashmap_free(u->stream_rules);
2177 - pa_modargs_free(ma);
2179 + pa_hashmap_free(u->groups);
2182 + if (u->volume_api)
2183 + pa_volume_api_unref(u->volume_api);
2189 - pa_modargs_free(ma);
2194 diff --git a/src/modules/main-volume-policy/main-volume-context.c b/src/modules/main-volume-policy/main-volume-context.c
2195 index 7ac35c6..9b9f9fd 100644
2196 --- a/src/modules/main-volume-policy/main-volume-context.c
2197 +++ b/src/modules/main-volume-policy/main-volume-context.c
2199 #include <modules/volume-api/mute-control.h>
2200 #include <modules/volume-api/volume-control.h>
2202 -int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
2203 - pa_main_volume_context **context) {
2204 - pa_main_volume_context *context_local;
2205 +#include <pulsecore/core-util.h>
2207 +int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, void *userdata, pa_main_volume_context **_r) {
2208 + pa_main_volume_context *context;
2213 - pa_assert(description);
2214 - pa_assert(context);
2217 - context_local = pa_xnew0(struct pa_main_volume_context, 1);
2218 - context_local->main_volume_policy = policy;
2219 - context_local->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
2220 + context = pa_xnew0(struct pa_main_volume_context, 1);
2221 + context->main_volume_policy = policy;
2222 + context->index = pa_main_volume_policy_allocate_main_volume_context_index(policy);
2224 - r = pa_main_volume_policy_register_name(policy, name, true, &context_local->name);
2225 + r = pa_main_volume_policy_register_name(policy, name, true, &context->name);
2229 - context_local->description = pa_xstrdup(description);
2231 - *context = context_local;
2232 + context->description = pa_xstrdup(context->name);
2233 + context->userdata = userdata;
2239 - pa_main_volume_context_free(context_local);
2241 + pa_main_volume_context_free(context);
2245 @@ -62,7 +63,6 @@ void pa_main_volume_context_put(pa_main_volume_context *context) {
2248 pa_main_volume_policy_add_main_volume_context(context->main_volume_policy, context);
2250 context->linked = true;
2252 pa_log_debug("Created main volume context #%u.", context->index);
2253 @@ -93,40 +93,21 @@ void pa_main_volume_context_unlink(pa_main_volume_context *context) {
2254 pa_log_debug("Unlinking main volume context %s.", context->name);
2256 if (context->linked)
2257 - pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
2259 - if (context->main_input_mute_control_binding) {
2260 - pa_binding_free(context->main_input_mute_control_binding);
2261 - context->main_input_mute_control_binding = NULL;
2263 + pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
2265 - if (context->main_output_mute_control_binding) {
2266 - pa_binding_free(context->main_output_mute_control_binding);
2267 - context->main_output_mute_control_binding = NULL;
2270 - if (context->main_input_volume_control_binding) {
2271 - pa_binding_free(context->main_input_volume_control_binding);
2272 - context->main_input_volume_control_binding = NULL;
2275 - if (context->main_output_volume_control_binding) {
2276 - pa_binding_free(context->main_output_volume_control_binding);
2277 - context->main_output_volume_control_binding = NULL;
2279 + pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK], context);
2281 context->main_input_mute_control = NULL;
2282 context->main_output_mute_control = NULL;
2283 context->main_input_volume_control = NULL;
2284 context->main_output_volume_control = NULL;
2286 - pa_main_volume_policy_remove_main_volume_context(context->main_volume_policy, context);
2289 void pa_main_volume_context_free(pa_main_volume_context *context) {
2292 - if (!context->unlinked)
2293 + /* unlink() expects name to be set. */
2294 + if (!context->unlinked && context->name)
2295 pa_main_volume_context_unlink(context);
2297 pa_xfree(context->description);
2298 @@ -137,13 +118,33 @@ void pa_main_volume_context_free(pa_main_volume_context *context) {
2302 -const char *pa_main_volume_context_get_name(pa_main_volume_context *context) {
2303 +void pa_main_volume_context_set_description(pa_main_volume_context *context, const char *description) {
2304 + char *old_description;
2307 + pa_assert(description);
2309 + old_description = context->description;
2311 + if (pa_streq(description, old_description))
2314 - return context->name;
2315 + context->description = pa_xstrdup(description);
2317 + if (!context->linked || context->unlinked) {
2318 + pa_xfree(old_description);
2322 + pa_log_debug("Main volume context %s description changed from \"%s\" to \"%s\".", context->name, old_description,
2324 + pa_xfree(old_description);
2326 + pa_hook_fire(&context->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_DESCRIPTION_CHANGED],
2330 -static void set_main_output_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
2331 +void pa_main_volume_context_set_main_output_volume_control(pa_main_volume_context *context, pa_volume_control *control) {
2332 pa_volume_control *old_control;
2335 @@ -158,7 +159,7 @@ static void set_main_output_volume_control_internal(pa_main_volume_context *cont
2336 if (!context->linked || context->unlinked)
2339 - pa_log_debug("The main output volume control of main volume context %s changed from %s to %s.", context->name,
2340 + pa_log_debug("Main volume context %s main output volume control changed from %s to %s.", context->name,
2341 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2343 pa_hook_fire(&context->main_volume_policy->hooks
2344 @@ -166,24 +167,7 @@ static void set_main_output_volume_control_internal(pa_main_volume_context *cont
2348 -void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
2349 - pa_binding_target_info *target_info) {
2350 - pa_binding_owner_info owner_info = {
2351 - .userdata = context,
2352 - .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
2355 - pa_assert(context);
2356 - pa_assert(target_info);
2358 - if (context->main_output_volume_control_binding)
2359 - pa_binding_free(context->main_output_volume_control_binding);
2361 - context->main_output_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2365 -static void set_main_input_volume_control_internal(pa_main_volume_context *context, pa_volume_control *control) {
2366 +void pa_main_volume_context_set_main_input_volume_control(pa_main_volume_context *context, pa_volume_control *control) {
2367 pa_volume_control *old_control;
2370 @@ -198,7 +182,7 @@ static void set_main_input_volume_control_internal(pa_main_volume_context *conte
2371 if (!context->linked || context->unlinked)
2374 - pa_log_debug("The main input volume control of main volume context %s changed from %s to %s.", context->name,
2375 + pa_log_debug("Main volume context %s main input volume control changed from %s to %s.", context->name,
2376 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2378 pa_hook_fire(&context->main_volume_policy->hooks
2379 @@ -206,24 +190,7 @@ static void set_main_input_volume_control_internal(pa_main_volume_context *conte
2383 -void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
2384 - pa_binding_target_info *target_info) {
2385 - pa_binding_owner_info owner_info = {
2386 - .userdata = context,
2387 - .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
2390 - pa_assert(context);
2391 - pa_assert(target_info);
2393 - if (context->main_input_volume_control_binding)
2394 - pa_binding_free(context->main_input_volume_control_binding);
2396 - context->main_input_volume_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2400 -static void set_main_output_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
2401 +void pa_main_volume_context_set_main_output_mute_control(pa_main_volume_context *context, pa_mute_control *control) {
2402 pa_mute_control *old_control;
2405 @@ -238,7 +205,7 @@ static void set_main_output_mute_control_internal(pa_main_volume_context *contex
2406 if (!context->linked || context->unlinked)
2409 - pa_log_debug("The main output mute control of main volume context %s changed from %s to %s.", context->name,
2410 + pa_log_debug("Main volume context %s main output mute control changed from %s to %s.", context->name,
2411 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2413 pa_hook_fire(&context->main_volume_policy->hooks
2414 @@ -246,24 +213,7 @@ static void set_main_output_mute_control_internal(pa_main_volume_context *contex
2418 -void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
2419 - pa_binding_target_info *target_info) {
2420 - pa_binding_owner_info owner_info = {
2421 - .userdata = context,
2422 - .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
2425 - pa_assert(context);
2426 - pa_assert(target_info);
2428 - if (context->main_output_mute_control_binding)
2429 - pa_binding_free(context->main_output_mute_control_binding);
2431 - context->main_output_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2435 -static void set_main_input_mute_control_internal(pa_main_volume_context *context, pa_mute_control *control) {
2436 +void pa_main_volume_context_set_main_input_mute_control(pa_main_volume_context *context, pa_mute_control *control) {
2437 pa_mute_control *old_control;
2440 @@ -278,48 +228,10 @@ static void set_main_input_mute_control_internal(pa_main_volume_context *context
2441 if (!context->linked || context->unlinked)
2444 - pa_log_debug("The main input mute control of main volume context %s changed from %s to %s.", context->name,
2445 + pa_log_debug("Main volume context %s main input mute control changed from %s to %s.", context->name,
2446 old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
2448 pa_hook_fire(&context->main_volume_policy->hooks
2449 [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
2453 -void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context,
2454 - pa_binding_target_info *target_info) {
2455 - pa_binding_owner_info owner_info = {
2456 - .userdata = context,
2457 - .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
2460 - pa_assert(context);
2461 - pa_assert(target_info);
2463 - if (context->main_input_mute_control_binding)
2464 - pa_binding_free(context->main_input_mute_control_binding);
2466 - context->main_input_mute_control_binding = pa_binding_new(context->main_volume_policy->volume_api, &owner_info,
2470 -pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy) {
2471 - pa_binding_target_type *type;
2473 - pa_assert(policy);
2475 - type = pa_binding_target_type_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, policy->main_volume_contexts,
2476 - &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT],
2477 - &policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK],
2478 - (pa_binding_target_type_get_name_cb_t) pa_main_volume_context_get_name);
2479 - pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL,
2480 - PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_volume_control));
2481 - pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL,
2482 - PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_volume_control));
2483 - pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL,
2484 - PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_output_mute_control));
2485 - pa_binding_target_type_add_field(type, PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL,
2486 - PA_BINDING_CALCULATE_FIELD_OFFSET(pa_main_volume_context, main_input_mute_control));
2490 diff --git a/src/modules/main-volume-policy/main-volume-context.h b/src/modules/main-volume-policy/main-volume-context.h
2491 index 4a0a6f7..3770168 100644
2492 --- a/src/modules/main-volume-policy/main-volume-context.h
2493 +++ b/src/modules/main-volume-policy/main-volume-context.h
2496 #include <modules/main-volume-policy/main-volume-policy.h>
2498 -#include <modules/volume-api/binding.h>
2500 typedef struct pa_main_volume_context pa_main_volume_context;
2502 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE "MainVolumeContext"
2503 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL "main_output_volume_control"
2504 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL "main_input_volume_control"
2505 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL "main_output_mute_control"
2506 -#define PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL "main_input_mute_control"
2508 struct pa_main_volume_context {
2509 pa_main_volume_policy *main_volume_policy;
2511 @@ -44,32 +36,21 @@ struct pa_main_volume_context {
2512 pa_mute_control *main_output_mute_control;
2513 pa_mute_control *main_input_mute_control;
2515 - pa_binding *main_output_volume_control_binding;
2516 - pa_binding *main_input_volume_control_binding;
2517 - pa_binding *main_output_mute_control_binding;
2518 - pa_binding *main_input_mute_control_binding;
2526 -int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, const char *description,
2527 - pa_main_volume_context **context);
2528 +int pa_main_volume_context_new(pa_main_volume_policy *policy, const char *name, void *userdata, pa_main_volume_context **_r);
2529 void pa_main_volume_context_put(pa_main_volume_context *context);
2530 void pa_main_volume_context_unlink(pa_main_volume_context *context);
2531 void pa_main_volume_context_free(pa_main_volume_context *context);
2533 -const char *pa_main_volume_context_get_name(pa_main_volume_context *context);
2535 -void pa_main_volume_context_bind_main_output_volume_control(pa_main_volume_context *context,
2536 - pa_binding_target_info *target_info);
2537 -void pa_main_volume_context_bind_main_input_volume_control(pa_main_volume_context *context,
2538 - pa_binding_target_info *target_info);
2539 -void pa_main_volume_context_bind_main_output_mute_control(pa_main_volume_context *context,
2540 - pa_binding_target_info *target_info);
2541 -void pa_main_volume_context_bind_main_input_mute_control(pa_main_volume_context *context, pa_binding_target_info *target_info);
2543 -/* Called from main-volume-policy.c only. */
2544 -pa_binding_target_type *pa_main_volume_context_create_binding_target_type(pa_main_volume_policy *policy);
2545 +void pa_main_volume_context_set_description(pa_main_volume_context *context, const char *description);
2546 +void pa_main_volume_context_set_main_output_volume_control(pa_main_volume_context *context, pa_volume_control *control);
2547 +void pa_main_volume_context_set_main_input_volume_control(pa_main_volume_context *context, pa_volume_control *control);
2548 +void pa_main_volume_context_set_main_output_mute_control(pa_main_volume_context *context, pa_mute_control *control);
2549 +void pa_main_volume_context_set_main_input_mute_control(pa_main_volume_context *context, pa_mute_control *control);
2552 diff --git a/src/modules/main-volume-policy/main-volume-policy.c b/src/modules/main-volume-policy/main-volume-policy.c
2553 index b0b4ede..3c0fccf 100644
2554 --- a/src/modules/main-volume-policy/main-volume-policy.c
2555 +++ b/src/modules/main-volume-policy/main-volume-policy.c
2557 #include <modules/main-volume-policy/main-volume-context.h>
2559 #include <pulsecore/core-util.h>
2560 +#include <pulsecore/namereg.h>
2561 #include <pulsecore/shared.h>
2563 static pa_main_volume_policy *main_volume_policy_new(pa_core *core);
2564 @@ -70,6 +71,46 @@ void pa_main_volume_policy_unref(pa_main_volume_policy *policy) {
2568 +static pa_hook_result_t volume_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2569 + pa_main_volume_policy *policy = userdata;
2570 + pa_volume_control *control = call_data;
2571 + pa_main_volume_context *context;
2574 + pa_assert(policy);
2575 + pa_assert(control);
2577 + PA_HASHMAP_FOREACH(context, policy->main_volume_contexts, state) {
2578 + if (context->main_output_volume_control == control)
2579 + pa_main_volume_context_set_main_output_volume_control(context, NULL);
2581 + if (context->main_input_volume_control == control)
2582 + pa_main_volume_context_set_main_input_volume_control(context, NULL);
2585 + return PA_HOOK_OK;
2588 +static pa_hook_result_t mute_control_unlink_cb(void *hook_data, void *call_data, void *userdata) {
2589 + pa_main_volume_policy *policy = userdata;
2590 + pa_mute_control *control = call_data;
2591 + pa_main_volume_context *context;
2594 + pa_assert(policy);
2595 + pa_assert(control);
2597 + PA_HASHMAP_FOREACH(context, policy->main_volume_contexts, state) {
2598 + if (context->main_output_mute_control == control)
2599 + pa_main_volume_context_set_main_output_mute_control(context, NULL);
2601 + if (context->main_input_mute_control == control)
2602 + pa_main_volume_context_set_main_input_mute_control(context, NULL);
2605 + return PA_HOOK_OK;
2608 static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
2609 pa_main_volume_policy *policy;
2611 @@ -86,8 +127,10 @@ static pa_main_volume_policy *main_volume_policy_new(pa_core *core) {
2612 for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
2613 pa_hook_init(&policy->hooks[i], policy);
2615 - policy->main_volume_context_binding_target_type = pa_main_volume_context_create_binding_target_type(policy);
2616 - pa_volume_api_add_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
2617 + policy->volume_control_unlink_slot = pa_hook_connect(&policy->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK],
2618 + PA_HOOK_NORMAL, volume_control_unlink_cb, policy);
2619 + policy->mute_control_unlink_slot = pa_hook_connect(&policy->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
2620 + PA_HOOK_NORMAL, mute_control_unlink_cb, policy);
2622 pa_log_debug("Created a pa_main_volume_policy object.");
2624 @@ -102,10 +145,11 @@ static void main_volume_policy_free(pa_main_volume_policy *policy) {
2626 pa_log_debug("Freeing the pa_main_volume_policy object.");
2628 - if (policy->main_volume_context_binding_target_type) {
2629 - pa_volume_api_remove_binding_target_type(policy->volume_api, policy->main_volume_context_binding_target_type);
2630 - pa_binding_target_type_free(policy->main_volume_context_binding_target_type);
2632 + if (policy->mute_control_unlink_slot)
2633 + pa_hook_slot_free(policy->mute_control_unlink_slot);
2635 + if (policy->volume_control_unlink_slot)
2636 + pa_hook_slot_free(policy->volume_control_unlink_slot);
2638 for (i = 0; i < PA_MAIN_VOLUME_POLICY_HOOK_MAX; i++)
2639 pa_hook_done(&policy->hooks[i]);
2640 @@ -134,19 +178,24 @@ int pa_main_volume_policy_register_name(pa_main_volume_policy *policy, const cha
2641 pa_assert(requested_name);
2642 pa_assert(registered_name);
2644 + if (!pa_namereg_is_valid_name(requested_name)) {
2645 + pa_log("Invalid name: \"%s\"", requested_name);
2646 + return -PA_ERR_INVALID;
2649 n = pa_xstrdup(requested_name);
2651 if (pa_hashmap_put(policy->names, n, n) < 0) {
2656 if (fail_if_already_registered) {
2658 pa_log("Name %s already registered.", requested_name);
2659 return -PA_ERR_EXIST;
2665 n = pa_sprintf_malloc("%s.%u", requested_name, i);
2666 } while (pa_hashmap_put(policy->names, n, n) < 0);
2667 diff --git a/src/modules/main-volume-policy/main-volume-policy.conf.example b/src/modules/main-volume-policy/main-volume-policy.conf.example
2668 index a4a35d3..3fcd267 100644
2669 --- a/src/modules/main-volume-policy/main-volume-policy.conf.example
2670 +++ b/src/modules/main-volume-policy/main-volume-policy.conf.example
2671 @@ -3,7 +3,6 @@ output-volume-model = by-active-main-volume-context
2672 input-volume-model = by-active-main-volume-context
2673 output-mute-model = none
2674 input-mute-model = none
2675 -main-volume-contexts = x-example-call-main-volume-context x-example-default-main-volume-context
2677 [MainVolumeContext x-example-call-main-volume-context]
2678 description = Call main volume context
2679 diff --git a/src/modules/main-volume-policy/main-volume-policy.h b/src/modules/main-volume-policy/main-volume-policy.h
2680 index 5cd669e..d5f6e02 100644
2681 --- a/src/modules/main-volume-policy/main-volume-policy.h
2682 +++ b/src/modules/main-volume-policy/main-volume-policy.h
2687 -#include <modules/volume-api/binding.h>
2688 #include <modules/volume-api/volume-api.h>
2690 #include <pulsecore/core.h>
2691 @@ -35,6 +34,7 @@ typedef struct pa_main_volume_context pa_main_volume_context;
2693 PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_PUT,
2694 PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_UNLINK,
2695 + PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_DESCRIPTION_CHANGED,
2696 PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
2697 PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED,
2698 PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED,
2699 @@ -53,7 +53,9 @@ struct pa_main_volume_policy {
2701 uint32_t next_main_volume_context_index;
2702 pa_hook hooks[PA_MAIN_VOLUME_POLICY_HOOK_MAX];
2703 - pa_binding_target_type *main_volume_context_binding_target_type;
2705 + pa_hook_slot *volume_control_unlink_slot;
2706 + pa_hook_slot *mute_control_unlink_slot;
2709 pa_main_volume_policy *pa_main_volume_policy_get(pa_core *core);
2710 diff --git a/src/modules/main-volume-policy/module-main-volume-policy.c b/src/modules/main-volume-policy/module-main-volume-policy.c
2711 index 0a89aa7..1b7693e 100644
2712 --- a/src/modules/main-volume-policy/module-main-volume-policy.c
2713 +++ b/src/modules/main-volume-policy/module-main-volume-policy.c
2716 #include <modules/main-volume-policy/main-volume-context.h>
2718 -#include <modules/volume-api/binding.h>
2719 +#include <modules/volume-api/audio-group.h>
2720 #include <modules/volume-api/volume-api.h>
2722 #include <pulse/direction.h>
2724 #include <pulsecore/core-util.h>
2725 #include <pulsecore/i18n.h>
2727 +#define BIND_PREFIX "bind:"
2728 +#define BIND_AUDIO_GROUP_PREFIX BIND_PREFIX "AudioGroup:"
2730 PA_MODULE_AUTHOR("Tanu Kaskinen");
2731 PA_MODULE_DESCRIPTION(_("Main volume and mute policy"));
2732 PA_MODULE_VERSION(PACKAGE_VERSION);
2733 @@ -52,6 +55,7 @@ enum model {
2737 + pa_volume_api *volume_api;
2738 pa_main_volume_policy *main_volume_policy;
2739 enum model output_volume_model;
2740 enum model input_volume_model;
2741 @@ -60,26 +64,67 @@ struct userdata {
2742 pa_hashmap *contexts; /* name -> struct context */
2744 pa_hook_slot *active_main_volume_context_changed_slot;
2745 + pa_hook_slot *main_volume_context_main_output_volume_control_changed_slot;
2746 + pa_hook_slot *main_volume_context_main_input_volume_control_changed_slot;
2747 + pa_hook_slot *main_volume_context_main_output_mute_control_changed_slot;
2748 + pa_hook_slot *main_volume_context_main_input_mute_control_changed_slot;
2749 + pa_hook_slot *audio_group_put_slot;
2750 + pa_hook_slot *audio_group_unlink_slot;
2751 + pa_hook_slot *audio_group_volume_control_changed_slot;
2752 + pa_hook_slot *audio_group_mute_control_changed_slot;
2755 - /* The following fields are only used during initialization. */
2756 - pa_hashmap *context_names; /* name -> name (hashmap-as-a-set) */
2757 - pa_hashmap *unused_contexts; /* name -> struct context */
2758 +struct control_info {
2759 + /* As appropriate for this control, points to one of
2760 + * - pa_main_volume_context.main_output_volume_control
2761 + * - pa_main_volume_context.main_input_volume_control
2762 + * - pa_main_volume_context.main_output_mute_control
2763 + * - pa_main_volume_context.main_input_mute_control */
2766 + /* As appropriate for this control, points to one of
2767 + * - userdata.output_volume_model
2768 + * - userdata.input_volume_model
2769 + * - userdata.output_mute_model
2770 + * - userdata.input_mute_model */
2771 + enum model *model;
2773 + /* Name of the audio group to which the context volume or mute control is
2774 + * bound. If the context control is not bound to anything, this is NULL. */
2775 + char *binding_target_name;
2777 + /* Points to the audio group to which the context volume or mute control is
2778 + * bound. If the context control is not bound to anything, or it's bound
2779 + * but the target doesn't currently exist, this is NULL. */
2780 + pa_audio_group *binding_target;
2782 + /* As appropriate for this control, points to one of
2783 + * - pa_main_volume_context_set_main_output_volume_control()
2784 + * - pa_main_volume_context_set_main_input_volume_control()
2785 + * - pa_main_volume_context_set_main_output_mute_control()
2786 + * - pa_main_volume_context_set_main_input_mute_control() */
2787 + void (*set_control)(pa_main_volume_context *context, void *control);
2789 + /* As appropriate for this control, points to one of
2790 + * - pa_volume_api_set_main_output_volume_control()
2791 + * - pa_volume_api_set_main_input_volume_control()
2792 + * - pa_volume_api_set_main_output_mute_control()
2793 + * - pa_volume_api_set_main_input_mute_control() */
2794 + void (*set_volume_api_control)(pa_volume_api *api, void *control);
2798 struct userdata *userdata;
2800 - char *description;
2801 - pa_binding_target_info *main_output_volume_control_target_info;
2802 - pa_binding_target_info *main_input_volume_control_target_info;
2803 - pa_binding_target_info *main_output_mute_control_target_info;
2804 - pa_binding_target_info *main_input_mute_control_target_info;
2805 pa_main_volume_context *main_volume_context;
2806 + struct control_info output_volume_info;
2807 + struct control_info input_volume_info;
2808 + struct control_info output_mute_info;
2809 + struct control_info input_mute_info;
2814 -static void context_unlink(struct context *context);
2815 +static void context_free(struct context *context);
2817 static const char *model_to_string(enum model model) {
2819 @@ -107,56 +152,57 @@ static int model_from_string(const char *str, enum model *model) {
2823 -static struct context *context_new(struct userdata *u, const char *name) {
2824 - struct context *context;
2825 +static int context_new(struct userdata *u, const char *name, struct context **_r) {
2826 + struct context *context = NULL;
2833 context = pa_xnew0(struct context, 1);
2834 context->userdata = u;
2835 - context->name = pa_xstrdup(name);
2836 - context->description = pa_xstrdup(name);
2841 -static int context_put(struct context *context) {
2844 - pa_assert(context);
2846 - r = pa_main_volume_context_new(context->userdata->main_volume_policy, context->name, context->description,
2847 - &context->main_volume_context);
2848 + r = pa_main_volume_context_new(u->main_volume_policy, name, u, &context->main_volume_context);
2852 - if (context->main_output_volume_control_target_info)
2853 - pa_main_volume_context_bind_main_output_volume_control(context->main_volume_context,
2854 - context->main_output_volume_control_target_info);
2855 + context->output_volume_info.control = (void **) &context->main_volume_context->main_output_volume_control;
2856 + context->input_volume_info.control = (void **) &context->main_volume_context->main_input_volume_control;
2857 + context->output_mute_info.control = (void **) &context->main_volume_context->main_output_mute_control;
2858 + context->input_mute_info.control = (void **) &context->main_volume_context->main_input_mute_control;
2860 - if (context->main_input_volume_control_target_info)
2861 - pa_main_volume_context_bind_main_input_volume_control(context->main_volume_context,
2862 - context->main_input_volume_control_target_info);
2863 + context->output_volume_info.model = &u->output_volume_model;
2864 + context->input_volume_info.model = &u->input_volume_model;
2865 + context->output_mute_info.model = &u->output_mute_model;
2866 + context->input_mute_info.model = &u->input_mute_model;
2868 - if (context->main_output_mute_control_target_info)
2869 - pa_main_volume_context_bind_main_output_mute_control(context->main_volume_context,
2870 - context->main_output_mute_control_target_info);
2871 + context->output_volume_info.set_control = (void *) pa_main_volume_context_set_main_output_volume_control;
2872 + context->input_volume_info.set_control = (void *) pa_main_volume_context_set_main_input_volume_control;
2873 + context->output_mute_info.set_control = (void *) pa_main_volume_context_set_main_output_mute_control;
2874 + context->input_mute_info.set_control = (void *) pa_main_volume_context_set_main_input_mute_control;
2876 - if (context->main_input_mute_control_target_info)
2877 - pa_main_volume_context_bind_main_input_mute_control(context->main_volume_context,
2878 - context->main_input_mute_control_target_info);
2880 - pa_main_volume_context_put(context->main_volume_context);
2881 + context->output_volume_info.set_volume_api_control = (void *) pa_volume_api_set_main_output_volume_control;
2882 + context->input_volume_info.set_volume_api_control = (void *) pa_volume_api_set_main_input_volume_control;
2883 + context->output_mute_info.set_volume_api_control = (void *) pa_volume_api_set_main_output_mute_control;
2884 + context->input_mute_info.set_volume_api_control = (void *) pa_volume_api_set_main_input_mute_control;
2890 - context_unlink(context);
2892 + context_free(context);
2897 +static void context_put(struct context *context) {
2898 + pa_assert(context);
2900 + pa_main_volume_context_put(context->main_volume_context);
2903 static void context_unlink(struct context *context) {
2906 @@ -165,10 +211,8 @@ static void context_unlink(struct context *context) {
2908 context->unlinked = true;
2910 - if (context->main_volume_context) {
2911 - pa_main_volume_context_free(context->main_volume_context);
2912 - context->main_volume_context = NULL;
2914 + if (context->main_volume_context)
2915 + pa_main_volume_context_unlink(context->main_volume_context);
2918 static void context_free(struct context *context) {
2919 @@ -177,132 +221,290 @@ static void context_free(struct context *context) {
2920 if (!context->unlinked)
2921 context_unlink(context);
2923 - if (context->main_input_mute_control_target_info)
2924 - pa_binding_target_info_free(context->main_input_mute_control_target_info);
2926 - if (context->main_output_mute_control_target_info)
2927 - pa_binding_target_info_free(context->main_output_mute_control_target_info);
2929 - if (context->main_input_volume_control_target_info)
2930 - pa_binding_target_info_free(context->main_input_volume_control_target_info);
2932 - if (context->main_output_volume_control_target_info)
2933 - pa_binding_target_info_free(context->main_output_volume_control_target_info);
2934 + if (context->main_volume_context)
2935 + pa_main_volume_context_free(context->main_volume_context);
2937 - pa_xfree(context->description);
2938 - pa_xfree(context->name);
2942 -static void context_set_description(struct context *context, const char *description) {
2943 - pa_assert(context);
2944 - pa_assert(description);
2946 - pa_xfree(context->description);
2947 - context->description = pa_xstrdup(description);
2950 -static void context_set_main_control_target_info(struct context *context, enum control_type type, pa_direction_t direction,
2951 - pa_binding_target_info *info) {
2952 +static struct control_info *context_get_control_info(struct context *context, enum control_type type,
2953 + pa_direction_t direction) {
2957 case CONTROL_TYPE_VOLUME:
2958 - if (direction == PA_DIRECTION_OUTPUT) {
2959 - if (context->main_output_volume_control_target_info)
2960 - pa_binding_target_info_free(context->main_output_volume_control_target_info);
2963 - context->main_output_volume_control_target_info = pa_binding_target_info_copy(info);
2965 - context->main_output_volume_control_target_info = NULL;
2967 - if (context->main_input_volume_control_target_info)
2968 - pa_binding_target_info_free(context->main_input_volume_control_target_info);
2971 - context->main_input_volume_control_target_info = pa_binding_target_info_copy(info);
2973 - context->main_input_volume_control_target_info = NULL;
2974 + switch (direction) {
2975 + case PA_DIRECTION_OUTPUT:
2976 + return &context->output_volume_info;
2978 + case PA_DIRECTION_INPUT:
2979 + return &context->input_volume_info;
2983 case CONTROL_TYPE_MUTE:
2984 - if (direction == PA_DIRECTION_OUTPUT) {
2985 - if (context->main_output_mute_control_target_info)
2986 - pa_binding_target_info_free(context->main_output_mute_control_target_info);
2989 - context->main_output_mute_control_target_info = pa_binding_target_info_copy(info);
2991 - context->main_output_mute_control_target_info = NULL;
2993 - if (context->main_input_mute_control_target_info)
2994 - pa_binding_target_info_free(context->main_input_mute_control_target_info);
2997 - context->main_input_mute_control_target_info = pa_binding_target_info_copy(info);
2999 - context->main_input_mute_control_target_info = NULL;
3000 + switch (direction) {
3001 + case PA_DIRECTION_OUTPUT:
3002 + return &context->output_mute_info;
3004 + case PA_DIRECTION_INPUT:
3005 + return &context->input_mute_info;
3010 + pa_assert_not_reached();
3013 +static void context_set_binding_target(struct context *context, enum control_type type, pa_direction_t direction,
3014 + pa_audio_group *group) {
3015 + struct control_info *info;
3016 + void *control = NULL;
3018 + pa_assert(context);
3020 + info = context_get_control_info(context, type, direction);
3021 + info->binding_target = group;
3025 + case CONTROL_TYPE_VOLUME:
3026 + control = group->volume_control;
3029 + case CONTROL_TYPE_MUTE:
3030 + control = group->mute_control;
3035 + info->set_control(context->main_volume_context, control);
3038 +static void context_set_binding_target_name(struct context *context, enum control_type type, pa_direction_t direction,
3039 + const char *name) {
3040 + struct control_info *info;
3041 + pa_audio_group *group = NULL;
3043 + pa_assert(context);
3045 + info = context_get_control_info(context, type, direction);
3047 + if (pa_safe_streq(name, info->binding_target_name))
3050 + pa_xfree(info->binding_target_name);
3051 + info->binding_target_name = pa_xstrdup(name);
3054 + group = pa_hashmap_get(context->userdata->volume_api->audio_groups, name);
3056 + context_set_binding_target(context, type, direction, group);
3059 static pa_hook_result_t active_main_volume_context_changed_cb(void *hook_data, void *call_data, void *userdata) {
3060 struct userdata *u = userdata;
3061 pa_main_volume_context *context;
3062 - pa_volume_api *api;
3063 - pa_binding_target_info *info;
3067 context = u->main_volume_policy->active_main_volume_context;
3068 - api = u->main_volume_policy->volume_api;
3070 if (u->output_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3072 - info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3073 - PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_VOLUME_CONTROL);
3074 - pa_volume_api_bind_main_output_volume_control(api, info);
3075 - pa_binding_target_info_free(info);
3077 - pa_volume_api_set_main_output_volume_control(api, NULL);
3079 + pa_volume_api_set_main_output_volume_control(u->volume_api, context->main_output_volume_control);
3081 + pa_volume_api_set_main_output_volume_control(u->volume_api, NULL);
3084 if (u->input_volume_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3086 - info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3087 - PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_VOLUME_CONTROL);
3088 - pa_volume_api_bind_main_input_volume_control(api, info);
3089 - pa_binding_target_info_free(info);
3091 - pa_volume_api_set_main_input_volume_control(api, NULL);
3093 + pa_volume_api_set_main_input_volume_control(u->volume_api, context->main_input_volume_control);
3095 + pa_volume_api_set_main_input_volume_control(u->volume_api, NULL);
3098 if (u->output_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3100 - info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3101 - PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_OUTPUT_MUTE_CONTROL);
3102 - pa_volume_api_bind_main_output_mute_control(api, info);
3103 - pa_binding_target_info_free(info);
3105 - pa_volume_api_set_main_output_mute_control(api, NULL);
3107 + pa_volume_api_set_main_output_mute_control(u->volume_api, context->main_output_mute_control);
3109 + pa_volume_api_set_main_output_mute_control(u->volume_api, NULL);
3112 if (u->input_mute_model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT) {
3114 - info = pa_binding_target_info_new(PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_TYPE, context->name,
3115 - PA_MAIN_VOLUME_CONTEXT_BINDING_TARGET_FIELD_MAIN_INPUT_MUTE_CONTROL);
3116 - pa_volume_api_bind_main_input_mute_control(api, info);
3117 - pa_binding_target_info_free(info);
3119 - pa_volume_api_set_main_input_mute_control(api, NULL);
3121 + pa_volume_api_set_main_input_mute_control(u->volume_api, context->main_input_mute_control);
3123 + pa_volume_api_set_main_input_mute_control(u->volume_api, NULL);
3126 + return PA_HOOK_OK;
3129 +static void handle_context_control_change(struct context *context, enum control_type type, pa_direction_t direction) {
3130 + struct control_info *info;
3132 + pa_assert(context);
3134 + info = context_get_control_info(context, type, direction);
3136 + if (*info->model == MODEL_BY_ACTIVE_MAIN_VOLUME_CONTEXT
3137 + && context->userdata->main_volume_policy->active_main_volume_context == context->main_volume_context)
3138 + info->set_volume_api_control(context->userdata->volume_api, *info->control);
3141 +static pa_hook_result_t main_volume_context_main_output_volume_control_changed_cb(void *hook_data, void *call_data,
3143 + pa_main_volume_context *context = call_data;
3145 + pa_assert(context);
3147 + handle_context_control_change(context->userdata, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
3149 + return PA_HOOK_OK;
3152 +static pa_hook_result_t main_volume_context_main_input_volume_control_changed_cb(void *hook_data, void *call_data,
3154 + pa_main_volume_context *context = call_data;
3156 + pa_assert(context);
3158 + handle_context_control_change(context->userdata, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
3160 + return PA_HOOK_OK;
3163 +static pa_hook_result_t main_volume_context_main_output_mute_control_changed_cb(void *hook_data, void *call_data,
3165 + pa_main_volume_context *context = call_data;
3167 + pa_assert(context);
3169 + handle_context_control_change(context->userdata, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
3171 + return PA_HOOK_OK;
3174 +static pa_hook_result_t main_volume_context_main_input_mute_control_changed_cb(void *hook_data, void *call_data,
3176 + pa_main_volume_context *context = call_data;
3178 + pa_assert(context);
3180 + handle_context_control_change(context->userdata, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
3182 + return PA_HOOK_OK;
3185 +static pa_hook_result_t audio_group_put_cb(void *hook_data, void *call_data, void *userdata) {
3186 + struct userdata *u = userdata;
3187 + pa_audio_group *group = call_data;
3188 + struct context *context;
3194 + PA_HASHMAP_FOREACH(context, u->contexts, state) {
3195 + if (context->output_volume_info.binding_target_name
3196 + && pa_streq(context->output_volume_info.binding_target_name, group->name))
3197 + context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT, group);
3199 + if (context->input_volume_info.binding_target_name
3200 + && pa_streq(context->input_volume_info.binding_target_name, group->name))
3201 + context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT, group);
3203 + if (context->output_mute_info.binding_target_name
3204 + && pa_streq(context->output_mute_info.binding_target_name, group->name))
3205 + context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT, group);
3207 + if (context->input_mute_info.binding_target_name
3208 + && pa_streq(context->input_mute_info.binding_target_name, group->name))
3209 + context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT, group);
3212 + return PA_HOOK_OK;
3215 +static pa_hook_result_t audio_group_unlink_cb(void *hook_data, void *call_data, void *userdata) {
3216 + struct userdata *u = userdata;
3217 + pa_audio_group *group = call_data;
3218 + struct context *context;
3224 + PA_HASHMAP_FOREACH(context, u->contexts, state) {
3225 + if (context->output_volume_info.binding_target == group)
3226 + context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT, NULL);
3228 + if (context->input_volume_info.binding_target == group)
3229 + context_set_binding_target(context, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT, NULL);
3231 + if (context->output_mute_info.binding_target == group)
3232 + context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT, NULL);
3234 + if (context->input_mute_info.binding_target == group)
3235 + context_set_binding_target(context, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT, NULL);
3241 +static void handle_audio_group_control_change(struct userdata *u, pa_audio_group *group, enum control_type type) {
3242 + struct context *context;
3248 + PA_HASHMAP_FOREACH(context, u->contexts, state) {
3250 + case CONTROL_TYPE_VOLUME:
3251 + if (context->output_volume_info.binding_target == group)
3252 + pa_main_volume_context_set_main_output_volume_control(context->main_volume_context, group->volume_control);
3254 + if (context->input_volume_info.binding_target == group)
3255 + pa_main_volume_context_set_main_input_volume_control(context->main_volume_context, group->volume_control);
3258 + case CONTROL_TYPE_MUTE:
3259 + if (context->output_mute_info.binding_target == group)
3260 + pa_main_volume_context_set_main_output_mute_control(context->main_volume_context, group->mute_control);
3262 + if (context->input_mute_info.binding_target == group)
3263 + pa_main_volume_context_set_main_input_mute_control(context->main_volume_context, group->mute_control);
3269 +static pa_hook_result_t audio_group_volume_control_changed_cb(void *hook_data, void *call_data, void *userdata) {
3270 + struct userdata *u = userdata;
3271 + pa_audio_group *group = call_data;
3276 + handle_audio_group_control_change(u, group, CONTROL_TYPE_VOLUME);
3278 + return PA_HOOK_OK;
3281 +static pa_hook_result_t audio_group_mute_control_changed_cb(void *hook_data, void *call_data, void *userdata) {
3282 + struct userdata *u = userdata;
3283 + pa_audio_group *group = call_data;
3288 + handle_audio_group_control_change(u, group, CONTROL_TYPE_MUTE);
3290 + return PA_HOOK_OK;
3293 static int parse_model(pa_config_parser_state *state) {
3296 @@ -315,105 +517,81 @@ static int parse_model(pa_config_parser_state *state) {
3300 -static int parse_main_volume_contexts(pa_config_parser_state *state) {
3301 - struct userdata *u;
3303 - const char *split_state = NULL;
3307 - u = state->userdata;
3309 - while ((name = pa_split_spaces(state->rvalue, &split_state)))
3310 - pa_hashmap_put(u->context_names, name, name);
3315 -static struct context *get_context(struct userdata *u, const char *section) {
3316 +static int get_context(struct userdata *u, const char *section, struct context **_r) {
3318 struct context *context;
3324 + return -PA_ERR_INVALID;
3326 if (!pa_startswith(section, "MainVolumeContext "))
3328 + return -PA_ERR_INVALID;
3330 name = section + 18;
3332 - context = pa_hashmap_get(u->unused_contexts, name);
3333 + context = pa_hashmap_get(u->contexts, name);
3335 - context = context_new(u, name);
3336 - pa_hashmap_put(u->unused_contexts, context->name, context);
3339 + r = context_new(u, name, &context);
3343 + pa_hashmap_put(u->contexts, (void *) context->main_volume_context->name, context);
3351 static int parse_description(pa_config_parser_state *state) {
3354 struct context *context;
3358 u = state->userdata;
3360 - context = get_context(u, state->section);
3362 - pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
3363 + r = get_context(u, state->section, &context);
3365 + pa_log("[%s:%u] Couldn't get main volume context for section \"%s\".", state->filename, state->lineno,
3366 pa_strnull(state->section));
3367 return -PA_ERR_INVALID;
3370 - context_set_description(context, state->rvalue);
3371 + pa_main_volume_context_set_description(context->main_volume_context, state->rvalue);
3376 -static const char *get_target_field_name(enum control_type type) {
3378 - case CONTROL_TYPE_VOLUME:
3379 - return "volume_control";
3381 - case CONTROL_TYPE_MUTE:
3382 - return "mute_control";
3385 - pa_assert_not_reached();
3388 -static int parse_main_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
3389 +static int parse_control(pa_config_parser_state *state, enum control_type type, pa_direction_t direction) {
3392 struct context *context;
3396 u = state->userdata;
3398 - context = get_context(u, state->section);
3400 - pa_log("[%s:%u] Key \"%s\" not expected in section %s.", state->filename, state->lineno, state->lvalue,
3401 + r = get_context(u, state->section, &context);
3403 + pa_log("[%s:%u] Couldn't get main volume context for section \"%s\".", state->filename, state->lineno,
3404 pa_strnull(state->section));
3405 return -PA_ERR_INVALID;
3408 if (pa_streq(state->rvalue, "none"))
3409 - context_set_main_control_target_info(context, type, direction, NULL);
3410 - else if (pa_startswith(state->rvalue, "bind:")) {
3412 - pa_binding_target_info *info;
3414 - r = pa_binding_target_info_new_from_string(state->rvalue, get_target_field_name(type), &info);
3416 - pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue);
3418 + context_set_binding_target_name(context, type, direction, NULL);
3419 + else if (pa_startswith(state->rvalue, BIND_PREFIX)) {
3420 + if (pa_startswith(state->rvalue, BIND_AUDIO_GROUP_PREFIX))
3421 + context_set_binding_target_name(context, type, direction, state->rvalue + strlen(BIND_AUDIO_GROUP_PREFIX));
3423 + pa_log("[%s:%u] Failed to parse binding target \"%s\".", state->filename, state->lineno, state->rvalue + strlen(BIND_PREFIX));
3424 + return -PA_ERR_INVALID;
3427 - context_set_main_control_target_info(context, type, direction, info);
3428 - pa_binding_target_info_free(info);
3430 pa_log("[%s:%u] Failed to parse value \"%s\".", state->filename, state->lineno, state->rvalue);
3431 return -PA_ERR_INVALID;
3432 @@ -425,69 +603,38 @@ static int parse_main_control(pa_config_parser_state *state, enum control_type t
3433 static int parse_main_output_volume_control(pa_config_parser_state *state) {
3436 - return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
3437 + return parse_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_OUTPUT);
3440 static int parse_main_input_volume_control(pa_config_parser_state *state) {
3443 - return parse_main_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
3444 + return parse_control(state, CONTROL_TYPE_VOLUME, PA_DIRECTION_INPUT);
3447 static int parse_main_output_mute_control(pa_config_parser_state *state) {
3450 - return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
3451 + return parse_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_OUTPUT);
3454 static int parse_main_input_mute_control(pa_config_parser_state *state) {
3457 - return parse_main_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
3460 -static void finalize_config(struct userdata *u) {
3461 - const char *context_name;
3463 - struct context *context;
3467 - PA_HASHMAP_FOREACH(context_name, u->context_names, state) {
3470 - context = pa_hashmap_remove(u->unused_contexts, context_name);
3472 - context = context_new(u, context_name);
3474 - r = context_put(context);
3476 - pa_log_warn("Failed to create main volume context %s.", context_name);
3477 - context_free(context);
3481 - pa_assert_se(pa_hashmap_put(u->contexts, context->name, context) >= 0);
3484 - PA_HASHMAP_FOREACH(context, u->unused_contexts, state)
3485 - pa_log_debug("Main volume context %s is not used.", context->name);
3487 - pa_hashmap_free(u->unused_contexts);
3488 - u->unused_contexts = NULL;
3490 - pa_hashmap_free(u->context_names);
3491 - u->context_names = NULL;
3492 + return parse_control(state, CONTROL_TYPE_MUTE, PA_DIRECTION_INPUT);
3495 int pa__init(pa_module *module) {
3499 + struct context *context;
3504 u = module->userdata = pa_xnew0(struct userdata, 1);
3505 + u->volume_api = pa_volume_api_get(module->core);
3506 u->main_volume_policy = pa_main_volume_policy_get(module->core);
3507 u->output_volume_model = MODEL_NONE;
3508 u->input_volume_model = MODEL_NONE;
3509 @@ -498,9 +645,32 @@ int pa__init(pa_module *module) {
3510 u->active_main_volume_context_changed_slot =
3511 pa_hook_connect(&u->main_volume_policy->hooks[PA_MAIN_VOLUME_POLICY_HOOK_ACTIVE_MAIN_VOLUME_CONTEXT_CHANGED],
3512 PA_HOOK_NORMAL, active_main_volume_context_changed_cb, u);
3513 - u->context_names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
3514 - u->unused_contexts = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
3515 - (pa_free_cb_t) context_free);
3516 + u->main_volume_context_main_output_volume_control_changed_slot =
3517 + pa_hook_connect(&u->main_volume_policy->hooks
3518 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED],
3519 + PA_HOOK_NORMAL, main_volume_context_main_output_volume_control_changed_cb, u);
3520 + u->main_volume_context_main_input_volume_control_changed_slot =
3521 + pa_hook_connect(&u->main_volume_policy->hooks
3522 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_VOLUME_CONTROL_CHANGED],
3523 + PA_HOOK_NORMAL, main_volume_context_main_input_volume_control_changed_cb, u);
3524 + u->main_volume_context_main_output_mute_control_changed_slot =
3525 + pa_hook_connect(&u->main_volume_policy->hooks
3526 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_OUTPUT_MUTE_CONTROL_CHANGED],
3527 + PA_HOOK_NORMAL, main_volume_context_main_output_mute_control_changed_cb, u);
3528 + u->main_volume_context_main_input_mute_control_changed_slot =
3529 + pa_hook_connect(&u->main_volume_policy->hooks
3530 + [PA_MAIN_VOLUME_POLICY_HOOK_MAIN_VOLUME_CONTEXT_MAIN_INPUT_MUTE_CONTROL_CHANGED],
3531 + PA_HOOK_NORMAL, main_volume_context_main_input_mute_control_changed_cb, u);
3532 + u->audio_group_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT], PA_HOOK_NORMAL,
3533 + audio_group_put_cb, u);
3534 + u->audio_group_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], PA_HOOK_NORMAL,
3535 + audio_group_unlink_cb, u);
3536 + u->audio_group_volume_control_changed_slot =
3537 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], PA_HOOK_NORMAL,
3538 + audio_group_volume_control_changed_cb, u);
3539 + u->audio_group_mute_control_changed_slot =
3540 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], PA_HOOK_NORMAL,
3541 + audio_group_mute_control_changed_cb, u);
3543 f = pa_open_config_file(PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "main-volume-policy.conf", "main-volume-policy.conf", NULL, &fn);
3545 @@ -509,7 +679,6 @@ int pa__init(pa_module *module) {
3546 { "input-volume-model", parse_model, &u->input_volume_model, "General" },
3547 { "output-mute-model", parse_model, &u->output_mute_model, "General" },
3548 { "input-mute-model", parse_model, &u->input_mute_model, "General" },
3549 - { "main-volume-contexts", parse_main_volume_contexts, NULL, "General" },
3550 { "description", parse_description, NULL, NULL },
3551 { "main-output-volume-control", parse_main_output_volume_control, NULL, NULL },
3552 { "main-input-volume-control", parse_main_input_volume_control, NULL, NULL },
3553 @@ -525,7 +694,8 @@ int pa__init(pa_module *module) {
3557 - finalize_config(u);
3558 + PA_HASHMAP_FOREACH(context, u->contexts, state)
3559 + context_put(context);
3561 pa_log_debug("Output volume model: %s", model_to_string(u->output_volume_model));
3562 pa_log_debug("Input volume model: %s", model_to_string(u->input_volume_model));
3563 @@ -544,6 +714,30 @@ void pa__done(pa_module *module) {
3567 + if (u->audio_group_mute_control_changed_slot)
3568 + pa_hook_slot_free(u->audio_group_mute_control_changed_slot);
3570 + if (u->audio_group_volume_control_changed_slot)
3571 + pa_hook_slot_free(u->audio_group_volume_control_changed_slot);
3573 + if (u->audio_group_unlink_slot)
3574 + pa_hook_slot_free(u->audio_group_unlink_slot);
3576 + if (u->audio_group_put_slot)
3577 + pa_hook_slot_free(u->audio_group_put_slot);
3579 + if (u->main_volume_context_main_input_mute_control_changed_slot)
3580 + pa_hook_slot_free(u->main_volume_context_main_input_mute_control_changed_slot);
3582 + if (u->main_volume_context_main_output_mute_control_changed_slot)
3583 + pa_hook_slot_free(u->main_volume_context_main_output_mute_control_changed_slot);
3585 + if (u->main_volume_context_main_input_volume_control_changed_slot)
3586 + pa_hook_slot_free(u->main_volume_context_main_input_volume_control_changed_slot);
3588 + if (u->main_volume_context_main_output_volume_control_changed_slot)
3589 + pa_hook_slot_free(u->main_volume_context_main_output_volume_control_changed_slot);
3591 if (u->active_main_volume_context_changed_slot)
3592 pa_hook_slot_free(u->active_main_volume_context_changed_slot);
3594 @@ -553,5 +747,8 @@ void pa__done(pa_module *module) {
3595 if (u->main_volume_policy)
3596 pa_main_volume_policy_unref(u->main_volume_policy);
3598 + if (u->volume_api)
3599 + pa_volume_api_unref(u->volume_api);
3603 diff --git a/src/modules/volume-api/audio-group.c b/src/modules/volume-api/audio-group.c
3604 index 76bfa69..66e0f8a 100644
3605 --- a/src/modules/volume-api/audio-group.c
3606 +++ b/src/modules/volume-api/audio-group.c
3609 #include <pulsecore/core-util.h>
3611 -int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group) {
3612 - pa_audio_group *group_local;
3613 +int pa_audio_group_new(pa_volume_api *api, const char *name, pa_audio_group **_r) {
3614 + pa_audio_group *group = NULL;
3619 - pa_assert(description);
3623 - group_local = pa_xnew0(pa_audio_group, 1);
3624 - group_local->volume_api = api;
3625 - group_local->index = pa_volume_api_allocate_audio_group_index(api);
3626 + group = pa_xnew0(pa_audio_group, 1);
3627 + group->volume_api = api;
3628 + group->index = pa_volume_api_allocate_audio_group_index(api);
3630 - r = pa_volume_api_register_name(api, name, true, &group_local->name);
3631 + r = pa_volume_api_register_name(api, name, true, &group->name);
3635 - group_local->description = pa_xstrdup(description);
3636 - group_local->proplist = pa_proplist_new();
3637 - group_local->volume_streams = pa_hashmap_new(NULL, NULL);
3638 - group_local->mute_streams = pa_hashmap_new(NULL, NULL);
3640 - *group = group_local;
3641 + group->description = pa_xstrdup(group->name);
3642 + group->proplist = pa_proplist_new();
3643 + group->volume_streams = pa_hashmap_new(NULL, NULL);
3644 + group->mute_streams = pa_hashmap_new(NULL, NULL);
3650 - pa_audio_group_free(group_local);
3652 + pa_audio_group_free(group);
3656 @@ -68,7 +67,6 @@ void pa_audio_group_put(pa_audio_group *group) {
3659 pa_volume_api_add_audio_group(group->volume_api, group);
3661 group->linked = true;
3663 pa_log_debug("Created audio group #%u.", group->index);
3664 @@ -99,9 +97,9 @@ void pa_audio_group_unlink(pa_audio_group *group) {
3665 pa_log_debug("Unlinking audio group %s.", group->name);
3668 - pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
3669 + pa_volume_api_remove_audio_group(group->volume_api, group);
3671 - pa_volume_api_remove_audio_group(group->volume_api, group);
3672 + pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK], group);
3674 while ((stream = pa_hashmap_first(group->mute_streams)))
3675 pas_stream_set_audio_group_for_mute(stream, NULL);
3676 @@ -109,34 +107,15 @@ void pa_audio_group_unlink(pa_audio_group *group) {
3677 while ((stream = pa_hashmap_first(group->volume_streams)))
3678 pas_stream_set_audio_group_for_volume(stream, NULL);
3680 - if (group->mute_control_binding) {
3681 - pa_binding_free(group->mute_control_binding);
3682 - group->mute_control_binding = NULL;
3685 - if (group->volume_control_binding) {
3686 - pa_binding_free(group->volume_control_binding);
3687 - group->volume_control_binding = NULL;
3690 - pa_audio_group_set_have_own_mute_control(group, false);
3691 - pa_audio_group_set_have_own_volume_control(group, false);
3693 - if (group->mute_control) {
3694 - pa_mute_control_remove_audio_group(group->mute_control, group);
3695 - group->mute_control = NULL;
3698 - if (group->volume_control) {
3699 - pa_volume_control_remove_audio_group(group->volume_control, group);
3700 - group->volume_control = NULL;
3702 + pa_audio_group_set_mute_control(group, NULL);
3703 + pa_audio_group_set_volume_control(group, NULL);
3706 void pa_audio_group_free(pa_audio_group *group) {
3709 - if (!group->unlinked)
3710 + /* unlink() expects name to be set. */
3711 + if (!group->unlinked && group->name)
3712 pa_audio_group_unlink(group);
3714 if (group->mute_streams)
3715 @@ -156,133 +135,33 @@ void pa_audio_group_free(pa_audio_group *group) {
3719 -const char *pa_audio_group_get_name(pa_audio_group *group) {
3721 +void pa_audio_group_set_description(pa_audio_group *group, const char *description) {
3722 + char *old_description;
3724 - return group->name;
3727 -static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume,
3728 - bool set_balance) {
3729 - pa_audio_group *group;
3730 - pas_stream *stream;
3733 - pa_assert(control);
3734 - pa_assert(volume);
3736 - group = control->userdata;
3738 - PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
3739 - if (stream->own_volume_control)
3740 - pa_volume_control_set_volume(stream->own_volume_control, volume, set_volume, set_balance);
3746 -static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
3747 - pa_audio_group *group;
3748 - pas_stream *stream;
3751 - pa_assert(control);
3753 - group = control->userdata;
3755 - PA_HASHMAP_FOREACH(stream, group->volume_streams, state) {
3756 - if (stream->own_volume_control)
3757 - pa_volume_control_set_volume(stream->own_volume_control, &control->volume, true, true);
3761 -void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have) {
3763 + pa_assert(description);
3765 - if (have == group->have_own_volume_control)
3769 - pa_bvolume initial_volume;
3771 - if (group->volume_api->core->flat_volumes)
3772 - /* Usually the initial volume should get overridden by some module
3773 - * that manages audio group volume levels, but if there's no such
3774 - * module, let's try to avoid too high volume in flat volume
3776 - pa_bvolume_init_mono(&initial_volume, 0.3 * PA_VOLUME_NORM);
3778 - pa_bvolume_init_mono(&initial_volume, PA_VOLUME_NORM);
3780 - pa_assert(!group->own_volume_control);
3781 - group->own_volume_control = pa_volume_control_new(group->volume_api, "audio-group-volume-control",
3782 - group->description, false, false);
3783 - pa_volume_control_set_owner_audio_group(group->own_volume_control, group);
3784 - group->own_volume_control->set_volume = volume_control_set_volume_cb;
3785 - group->own_volume_control->userdata = group;
3786 - pa_volume_control_put(group->own_volume_control, &initial_volume, volume_control_set_initial_volume_cb);
3788 - pa_volume_control_free(group->own_volume_control);
3789 - group->own_volume_control = NULL;
3792 - group->have_own_volume_control = have;
3795 -static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
3796 - pa_audio_group *group;
3797 - pas_stream *stream;
3800 - pa_assert(control);
3802 - group = control->userdata;
3804 - PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
3805 - if (stream->own_mute_control)
3806 - pa_mute_control_set_mute(stream->own_mute_control, mute);
3812 -static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
3813 - pa_audio_group *group;
3814 - pas_stream *stream;
3816 + old_description = group->description;
3818 - pa_assert(control);
3819 + if (pa_streq(description, old_description))
3822 - group = control->userdata;
3823 + group->description = pa_xstrdup(description);
3825 - PA_HASHMAP_FOREACH(stream, group->mute_streams, state) {
3826 - if (stream->own_mute_control)
3827 - pa_mute_control_set_mute(stream->own_mute_control, control->mute);
3828 + if (!group->linked || group->unlinked) {
3829 + pa_xfree(old_description);
3834 -void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have) {
3836 + pa_log_debug("The description of audio group %s changed from \"%s\" to \"%s\".", group->name, old_description,
3838 + pa_xfree(old_description);
3840 - if (have == group->have_own_mute_control)
3842 + pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED], group);
3844 - group->have_own_mute_control = have;
3847 - pa_assert(!group->own_mute_control);
3848 - group->own_mute_control = pa_mute_control_new(group->volume_api, "audio-group-mute-control", group->description);
3849 - pa_mute_control_set_owner_audio_group(group->own_mute_control, group);
3850 - group->own_mute_control->set_mute = mute_control_set_mute_cb;
3851 - group->own_mute_control->userdata = group;
3852 - pa_mute_control_put(group->own_mute_control, false, true, mute_control_set_initial_mute_cb);
3854 - pa_mute_control_free(group->own_mute_control);
3855 - group->own_mute_control = NULL;
3859 -static void set_volume_control_internal(pa_audio_group *group, pa_volume_control *control) {
3860 +void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
3861 pa_volume_control *old_control;
3864 @@ -292,14 +171,8 @@ static void set_volume_control_internal(pa_audio_group *group, pa_volume_control
3865 if (control == old_control)
3869 - pa_volume_control_remove_audio_group(old_control, group);
3871 group->volume_control = control;
3874 - pa_volume_control_add_audio_group(control, group);
3876 if (!group->linked || group->unlinked)
3879 @@ -309,18 +182,7 @@ static void set_volume_control_internal(pa_audio_group *group, pa_volume_control
3880 pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED], group);
3883 -void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control) {
3886 - if (group->volume_control_binding) {
3887 - pa_binding_free(group->volume_control_binding);
3888 - group->volume_control_binding = NULL;
3891 - set_volume_control_internal(group, control);
3894 -static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *control) {
3895 +void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
3896 pa_mute_control *old_control;
3899 @@ -330,14 +192,8 @@ static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *co
3900 if (control == old_control)
3904 - pa_mute_control_remove_audio_group(old_control, group);
3906 group->mute_control = control;
3909 - pa_mute_control_add_audio_group(control, group);
3911 if (!group->linked || group->unlinked)
3914 @@ -347,57 +203,11 @@ static void set_mute_control_internal(pa_audio_group *group, pa_mute_control *co
3915 pa_hook_fire(&group->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED], group);
3918 -void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control) {
3921 - if (group->mute_control_binding) {
3922 - pa_binding_free(group->mute_control_binding);
3923 - group->mute_control_binding = NULL;
3926 - set_mute_control_internal(group, control);
3929 -void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info) {
3930 - pa_binding_owner_info owner_info = {
3931 - .userdata = group,
3932 - .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
3936 - pa_assert(target_info);
3938 - if (group->volume_control_binding)
3939 - pa_binding_free(group->volume_control_binding);
3941 - group->volume_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
3944 -void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info) {
3945 - pa_binding_owner_info owner_info = {
3946 - .userdata = group,
3947 - .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
3951 - pa_assert(target_info);
3953 - if (group->mute_control_binding)
3954 - pa_binding_free(group->mute_control_binding);
3956 - group->mute_control_binding = pa_binding_new(group->volume_api, &owner_info, target_info);
3959 void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream) {
3963 pa_assert_se(pa_hashmap_put(group->volume_streams, stream, stream) >= 0);
3965 - if (stream->own_volume_control && group->own_volume_control)
3966 - pa_volume_control_set_volume(stream->own_volume_control, &group->own_volume_control->volume, true, true);
3968 - pa_log_debug("Stream %s added to audio group %s (volume).", stream->name, group->name);
3971 void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream) {
3972 @@ -405,8 +215,6 @@ void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stre
3975 pa_assert_se(pa_hashmap_remove(group->volume_streams, stream));
3977 - pa_log_debug("Stream %s removed from audio group %s (volume).", stream->name, group->name);
3980 void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
3981 @@ -414,11 +222,6 @@ void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream) {
3984 pa_assert_se(pa_hashmap_put(group->mute_streams, stream, stream) >= 0);
3986 - if (stream->own_mute_control && group->own_mute_control)
3987 - pa_mute_control_set_mute(stream->own_mute_control, group->own_mute_control->mute);
3989 - pa_log_debug("Stream %s added to audio group %s (mute).", stream->name, group->name);
3992 void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream) {
3993 @@ -426,23 +229,4 @@ void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream
3996 pa_assert_se(pa_hashmap_remove(group->mute_streams, stream));
3998 - pa_log_debug("Stream %s removed from audio group %s (mute).", stream->name, group->name);
4001 -pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api) {
4002 - pa_binding_target_type *type;
4006 - type = pa_binding_target_type_new(PA_AUDIO_GROUP_BINDING_TARGET_TYPE, api->audio_groups,
4007 - &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT],
4008 - &api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
4009 - (pa_binding_target_type_get_name_cb_t) pa_audio_group_get_name);
4010 - pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL,
4011 - PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, volume_control));
4012 - pa_binding_target_type_add_field(type, PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL,
4013 - PA_BINDING_CALCULATE_FIELD_OFFSET(pa_audio_group, mute_control));
4017 diff --git a/src/modules/volume-api/audio-group.h b/src/modules/volume-api/audio-group.h
4018 index 41591ba..02db3eb 100644
4019 --- a/src/modules/volume-api/audio-group.h
4020 +++ b/src/modules/volume-api/audio-group.h
4025 -#include <modules/volume-api/binding.h>
4026 #include <modules/volume-api/mute-control.h>
4027 #include <modules/volume-api/volume-control.h>
4031 typedef struct pa_audio_group pa_audio_group;
4033 -#define PA_AUDIO_GROUP_BINDING_TARGET_TYPE "AudioGroup"
4034 -#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_VOLUME_CONTROL "volume_control"
4035 -#define PA_AUDIO_GROUP_BINDING_TARGET_FIELD_MUTE_CONTROL "mute_control"
4037 struct pa_audio_group {
4038 pa_volume_api *volume_api;
4040 @@ -44,13 +39,7 @@ struct pa_audio_group {
4041 pa_proplist *proplist;
4042 pa_volume_control *volume_control;
4043 pa_mute_control *mute_control;
4044 - bool have_own_volume_control;
4045 - bool have_own_mute_control;
4046 - pa_volume_control *own_volume_control;
4047 - pa_mute_control *own_mute_control;
4049 - pa_binding *volume_control_binding;
4050 - pa_binding *mute_control_binding;
4051 pa_hashmap *volume_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
4052 pa_hashmap *mute_streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
4054 @@ -58,28 +47,22 @@ struct pa_audio_group {
4058 -int pa_audio_group_new(pa_volume_api *api, const char *name, const char *description, pa_audio_group **group);
4059 +int pa_audio_group_new(pa_volume_api *api, const char *name, pa_audio_group **_r);
4060 void pa_audio_group_put(pa_audio_group *group);
4061 void pa_audio_group_unlink(pa_audio_group *group);
4062 void pa_audio_group_free(pa_audio_group *group);
4064 -const char *pa_audio_group_get_name(pa_audio_group *group);
4066 -/* Called by policy modules. */
4067 -void pa_audio_group_set_have_own_volume_control(pa_audio_group *group, bool have);
4068 -void pa_audio_group_set_have_own_mute_control(pa_audio_group *group, bool have);
4069 +/* Called by the audio group implementation. */
4070 +void pa_audio_group_set_description(pa_audio_group *group, const char *description);
4071 void pa_audio_group_set_volume_control(pa_audio_group *group, pa_volume_control *control);
4072 void pa_audio_group_set_mute_control(pa_audio_group *group, pa_mute_control *control);
4073 -void pa_audio_group_bind_volume_control(pa_audio_group *group, pa_binding_target_info *target_info);
4074 -void pa_audio_group_bind_mute_control(pa_audio_group *group, pa_binding_target_info *target_info);
4076 -/* Called from sstream.c only. */
4077 +/* Called by sstream.c only. If you want to assign a stream to an audio group, use
4078 + * pas_stream_set_audio_group_for_volume() and
4079 + * pas_stream_set_audio_group_for_mute(). */
4080 void pa_audio_group_add_volume_stream(pa_audio_group *group, pas_stream *stream);
4081 void pa_audio_group_remove_volume_stream(pa_audio_group *group, pas_stream *stream);
4082 void pa_audio_group_add_mute_stream(pa_audio_group *group, pas_stream *stream);
4083 void pa_audio_group_remove_mute_stream(pa_audio_group *group, pas_stream *stream);
4085 -/* Called from volume-api.c only. */
4086 -pa_binding_target_type *pa_audio_group_create_binding_target_type(pa_volume_api *api);
4089 diff --git a/src/modules/volume-api/binding.c b/src/modules/volume-api/binding.c
4090 deleted file mode 100644
4091 index 6e73119..0000000
4092 --- a/src/modules/volume-api/binding.c
4096 - This file is part of PulseAudio.
4098 - Copyright 2014 Intel Corporation
4100 - PulseAudio is free software; you can redistribute it and/or modify
4101 - it under the terms of the GNU Lesser General Public License as published
4102 - by the Free Software Foundation; either version 2.1 of the License,
4103 - or (at your option) any later version.
4105 - PulseAudio is distributed in the hope that it will be useful, but
4106 - WITHOUT ANY WARRANTY; without even the implied warranty of
4107 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4108 - General Public License for more details.
4110 - You should have received a copy of the GNU Lesser General Public License
4111 - along with PulseAudio; if not, write to the Free Software
4112 - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4116 -#ifdef HAVE_CONFIG_H
4117 -#include <config.h>
4120 -#include "binding.h"
4122 -#include <pulse/def.h>
4123 -#include <pulse/xmalloc.h>
4125 -#include <pulsecore/core-util.h>
4126 -#include <pulsecore/macro.h>
4128 -struct field_entry {
4133 -static void set_target_type(pa_binding *binding, pa_binding_target_type *type);
4134 -static void set_target_object(pa_binding *binding, void *object);
4136 -pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata) {
4137 - pa_binding_owner_info *info;
4139 - pa_assert(set_value);
4141 - info = pa_xnew0(pa_binding_owner_info, 1);
4142 - info->set_value = set_value;
4143 - info->userdata = userdata;
4148 -pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info) {
4151 - return pa_binding_owner_info_new(info->set_value, info->userdata);
4154 -void pa_binding_owner_info_free(pa_binding_owner_info *info) {
4160 -pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field) {
4161 - pa_binding_target_info *info;
4167 - info = pa_xnew0(pa_binding_target_info, 1);
4168 - info->type = pa_xstrdup(type);
4169 - info->name = pa_xstrdup(name);
4170 - info->field = pa_xstrdup(field);
4175 -int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info) {
4176 - const char *colon;
4177 - char *type = NULL;
4178 - char *name = NULL;
4184 - if (!pa_startswith(str, "bind:"))
4187 - colon = strchr(str + 5, ':');
4191 - type = pa_xstrndup(str + 5, colon - (str + 5));
4196 - name = pa_xstrdup(colon + 1);
4201 - *info = pa_binding_target_info_new(type, name, field);
4208 - pa_log("Invalid binding target: %s", str);
4212 - return -PA_ERR_INVALID;
4215 -pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info) {
4218 - return pa_binding_target_info_new(info->type, info->name, info->field);
4221 -void pa_binding_target_info_free(pa_binding_target_info *info) {
4224 - pa_xfree(info->field);
4225 - pa_xfree(info->name);
4226 - pa_xfree(info->type);
4230 -static void field_entry_free(struct field_entry *entry) {
4233 - pa_xfree(entry->name);
4237 -pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
4238 - pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name) {
4239 - pa_binding_target_type *type;
4242 - pa_assert(objects);
4243 - pa_assert(put_hook);
4244 - pa_assert(unlink_hook);
4245 - pa_assert(get_name);
4247 - type = pa_xnew0(pa_binding_target_type, 1);
4248 - type->name = pa_xstrdup(name);
4249 - type->objects = objects;
4250 - type->put_hook = put_hook;
4251 - type->unlink_hook = unlink_hook;
4252 - type->get_name = get_name;
4253 - type->fields = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) field_entry_free);
4258 -void pa_binding_target_type_free(pa_binding_target_type *type) {
4262 - pa_hashmap_free(type->fields);
4264 - pa_xfree(type->name);
4268 -void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset) {
4269 - struct field_entry *entry;
4274 - entry = pa_xnew0(struct field_entry, 1);
4275 - entry->name = pa_xstrdup(name);
4276 - entry->offset = offset;
4278 - pa_assert_se(pa_hashmap_put(type->fields, entry->name, entry) >= 0);
4281 -int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset) {
4282 - struct field_entry *entry;
4286 - pa_assert(offset);
4288 - entry = pa_hashmap_get(type->fields, field);
4290 - return -PA_ERR_NOENTITY;
4292 - *offset = entry->offset;
4297 -static pa_hook_result_t target_type_added_cb(void *hook_data, void *call_data, void *userdata) {
4298 - pa_binding_target_type *type = call_data;
4299 - pa_binding *binding = userdata;
4302 - pa_assert(binding);
4304 - if (!pa_streq(type->name, binding->target_info->type))
4305 - return PA_HOOK_OK;
4307 - set_target_type(binding, type);
4309 - return PA_HOOK_OK;
4312 -static pa_hook_result_t target_type_removed_cb(void *hook_data, void *call_data, void *userdata) {
4313 - pa_binding_target_type *type = call_data;
4314 - pa_binding *binding = userdata;
4317 - pa_assert(binding);
4319 - if (type != binding->target_type)
4320 - return PA_HOOK_OK;
4322 - set_target_type(binding, NULL);
4324 - return PA_HOOK_OK;
4327 -static pa_hook_result_t target_put_cb(void *hook_data, void *call_data, void *userdata) {
4328 - pa_binding *binding = userdata;
4330 - pa_assert(call_data);
4331 - pa_assert(binding);
4333 - if (!pa_streq(binding->target_type->get_name(call_data), binding->target_info->name))
4334 - return PA_HOOK_OK;
4336 - set_target_object(binding, call_data);
4338 - return PA_HOOK_OK;
4341 -static pa_hook_result_t target_unlink_cb(void *hook_data, void *call_data, void *userdata) {
4342 - pa_binding *binding = userdata;
4344 - pa_assert(call_data);
4345 - pa_assert(binding);
4347 - if (call_data != binding->target_object)
4348 - return PA_HOOK_OK;
4350 - set_target_object(binding, NULL);
4352 - return PA_HOOK_OK;
4355 -static void set_target_object(pa_binding *binding, void *object) {
4356 - pa_assert(binding);
4358 - binding->target_object = object;
4361 - if (binding->target_put_slot) {
4362 - pa_hook_slot_free(binding->target_put_slot);
4363 - binding->target_put_slot = NULL;
4366 - if (!binding->target_unlink_slot)
4367 - binding->target_unlink_slot = pa_hook_connect(binding->target_type->unlink_hook, PA_HOOK_NORMAL, target_unlink_cb,
4370 - if (binding->target_field_offset_valid)
4371 - binding->owner_info->set_value(binding->owner_info->userdata,
4372 - *((void **) (((uint8_t *) object) + binding->target_field_offset)));
4374 - binding->owner_info->set_value(binding->owner_info->userdata, NULL);
4376 - if (binding->target_unlink_slot) {
4377 - pa_hook_slot_free(binding->target_unlink_slot);
4378 - binding->target_unlink_slot = NULL;
4381 - if (binding->target_type) {
4382 - if (!binding->target_put_slot)
4383 - binding->target_put_slot = pa_hook_connect(binding->target_type->put_hook, PA_HOOK_NORMAL, target_put_cb, binding);
4385 - if (binding->target_put_slot) {
4386 - pa_hook_slot_free(binding->target_put_slot);
4387 - binding->target_put_slot = NULL;
4391 - binding->owner_info->set_value(binding->owner_info->userdata, NULL);
4395 -static void set_target_type(pa_binding *binding, pa_binding_target_type *type) {
4396 - pa_assert(binding);
4398 - binding->target_type = type;
4403 - if (binding->target_type_added_slot) {
4404 - pa_hook_slot_free(binding->target_type_added_slot);
4405 - binding->target_type_added_slot = NULL;
4408 - if (!binding->target_type_removed_slot)
4409 - binding->target_type_removed_slot =
4410 - pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED],
4411 - PA_HOOK_NORMAL, target_type_removed_cb, binding);
4413 - r = pa_binding_target_type_get_field_offset(type, binding->target_info->field, &binding->target_field_offset);
4415 - binding->target_field_offset_valid = true;
4417 - pa_log_warn("Reference to non-existing field \"%s\" in binding target type \"%s\".", binding->target_info->field,
4419 - binding->target_field_offset_valid = false;
4422 - set_target_object(binding, pa_hashmap_get(type->objects, binding->target_info->name));
4424 - if (binding->target_type_removed_slot) {
4425 - pa_hook_slot_free(binding->target_type_removed_slot);
4426 - binding->target_type_removed_slot = NULL;
4429 - if (!binding->target_type_added_slot)
4430 - binding->target_type_added_slot =
4431 - pa_hook_connect(&binding->volume_api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED],
4432 - PA_HOOK_NORMAL, target_type_added_cb, binding);
4434 - binding->target_field_offset_valid = false;
4436 - set_target_object(binding, NULL);
4440 -pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
4441 - const pa_binding_target_info *target_info) {
4442 - pa_binding *binding;
4445 - pa_assert(owner_info);
4446 - pa_assert(target_info);
4448 - binding = pa_xnew0(pa_binding, 1);
4449 - binding->volume_api = api;
4450 - binding->owner_info = pa_binding_owner_info_copy(owner_info);
4451 - binding->target_info = pa_binding_target_info_copy(target_info);
4453 - set_target_type(binding, pa_hashmap_get(api->binding_target_types, target_info->type));
4458 -void pa_binding_free(pa_binding *binding) {
4459 - pa_assert(binding);
4461 - if (binding->target_unlink_slot)
4462 - pa_hook_slot_free(binding->target_unlink_slot);
4464 - if (binding->target_put_slot)
4465 - pa_hook_slot_free(binding->target_put_slot);
4467 - if (binding->target_type_removed_slot)
4468 - pa_hook_slot_free(binding->target_type_removed_slot);
4470 - if (binding->target_type_added_slot)
4471 - pa_hook_slot_free(binding->target_type_added_slot);
4473 - if (binding->target_info)
4474 - pa_binding_target_info_free(binding->target_info);
4476 - if (binding->owner_info)
4477 - pa_binding_owner_info_free(binding->owner_info);
4479 - pa_xfree(binding);
4481 diff --git a/src/modules/volume-api/binding.h b/src/modules/volume-api/binding.h
4482 deleted file mode 100644
4483 index ba4dea8..0000000
4484 --- a/src/modules/volume-api/binding.h
4487 -#ifndef foobindinghfoo
4488 -#define foobindinghfoo
4491 - This file is part of PulseAudio.
4493 - Copyright 2014 Intel Corporation
4495 - PulseAudio is free software; you can redistribute it and/or modify
4496 - it under the terms of the GNU Lesser General Public License as published
4497 - by the Free Software Foundation; either version 2.1 of the License,
4498 - or (at your option) any later version.
4500 - PulseAudio is distributed in the hope that it will be useful, but
4501 - WITHOUT ANY WARRANTY; without even the implied warranty of
4502 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4503 - General Public License for more details.
4505 - You should have received a copy of the GNU Lesser General Public License
4506 - along with PulseAudio; if not, write to the Free Software
4507 - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4511 -#include <modules/volume-api/volume-api.h>
4513 -typedef struct pa_binding pa_binding;
4514 -typedef struct pa_binding_owner_info pa_binding_owner_info;
4515 -typedef struct pa_binding_target_info pa_binding_target_info;
4516 -typedef struct pa_binding_target_type pa_binding_target_type;
4518 -typedef void (*pa_binding_set_value_cb_t)(void *userdata, void *value);
4520 -struct pa_binding_owner_info {
4521 - /* This is the object that has the variable that the binding is created
4525 - /* Called when the owner object's value needs to be updated. The userdata
4526 - * parameter of the callback is the same as the userdata field in this
4527 - * struct, and the value parameter is the new value for whatever variable
4528 - * the binding was created for. */
4529 - pa_binding_set_value_cb_t set_value;
4532 -pa_binding_owner_info *pa_binding_owner_info_new(pa_binding_set_value_cb_t set_value, void *userdata);
4533 -pa_binding_owner_info *pa_binding_owner_info_copy(const pa_binding_owner_info *info);
4534 -void pa_binding_owner_info_free(pa_binding_owner_info *info);
4536 -struct pa_binding_target_info {
4537 - /* The target type name as registered with
4538 - * pa_binding_target_type_register(). */
4541 - /* The target object name as returned by the get_name callback of
4542 - * pa_binding_target_type. */
4545 - /* The target field of the target object. */
4549 -pa_binding_target_info *pa_binding_target_info_new(const char *type, const char *name, const char *field);
4551 -/* The string format is "bind:TYPE:NAME". */
4552 -int pa_binding_target_info_new_from_string(const char *str, const char *field, pa_binding_target_info **info);
4554 -pa_binding_target_info *pa_binding_target_info_copy(const pa_binding_target_info *info);
4555 -void pa_binding_target_info_free(pa_binding_target_info *info);
4557 -typedef const char *(*pa_binding_target_type_get_name_cb_t)(void *object);
4559 -struct pa_binding_target_type {
4560 - /* Identifier for this target type. */
4563 - /* name -> object. Points directly to some "master" object hashmap, so the
4564 - * hashmap is not owned by pa_binding_target_type. */
4565 - pa_hashmap *objects;
4567 - /* The hook that notifies of new objects if this target type. The call data
4568 - * of the hook must be a pointer to the new object (this should be true for
4569 - * all PUT hooks, so don't worry too much). */
4570 - pa_hook *put_hook;
4572 - /* The hook that notifies of unlinked objects of this target type. The call
4573 - * data of the hook must be a pointer to the removed object (this should be
4574 - * true for all UNLINK hooks, so don't worry too much). */
4575 - pa_hook *unlink_hook;
4577 - /* Function for getting the name of an object of this target type. */
4578 - pa_binding_target_type_get_name_cb_t get_name;
4580 - pa_hashmap *fields;
4583 -pa_binding_target_type *pa_binding_target_type_new(const char *name, pa_hashmap *objects, pa_hook *put_hook,
4584 - pa_hook *unlink_hook, pa_binding_target_type_get_name_cb_t get_name);
4585 -void pa_binding_target_type_free(pa_binding_target_type *type);
4587 -/* Useful when calling pa_binding_target_type_add_field(). */
4588 -#define PA_BINDING_CALCULATE_FIELD_OFFSET(type, field) ((size_t) &(((type *) 0)->field))
4590 -/* Called during the type initialization (right after
4591 - * pa_binding_target_type_new()). */
4592 -void pa_binding_target_type_add_field(pa_binding_target_type *type, const char *name, size_t offset);
4594 -int pa_binding_target_type_get_field_offset(pa_binding_target_type *type, const char *field, size_t *offset);
4596 -struct pa_binding {
4597 - pa_volume_api *volume_api;
4598 - pa_binding_owner_info *owner_info;
4599 - pa_binding_target_info *target_info;
4600 - pa_binding_target_type *target_type;
4601 - void *target_object;
4602 - size_t target_field_offset;
4603 - bool target_field_offset_valid;
4604 - pa_hook_slot *target_type_added_slot;
4605 - pa_hook_slot *target_type_removed_slot;
4606 - pa_hook_slot *target_put_slot;
4607 - pa_hook_slot *target_unlink_slot;
4610 -pa_binding *pa_binding_new(pa_volume_api *api, const pa_binding_owner_info *owner_info,
4611 - const pa_binding_target_info *target_info);
4612 -void pa_binding_free(pa_binding *binding);
4615 diff --git a/src/modules/volume-api/bvolume.h b/src/modules/volume-api/bvolume.h
4616 index 0317fb6..75545dd 100644
4617 --- a/src/modules/volume-api/bvolume.h
4618 +++ b/src/modules/volume-api/bvolume.h
4619 @@ -29,13 +29,16 @@ typedef pa_ext_volume_api_bvolume pa_bvolume;
4620 #define pa_balance_valid pa_ext_volume_api_balance_valid
4621 #define pa_bvolume_valid pa_ext_volume_api_bvolume_valid
4622 #define pa_bvolume_init_invalid pa_ext_volume_api_bvolume_init_invalid
4623 +#define pa_bvolume_init pa_ext_volume_api_bvolume_init
4624 #define pa_bvolume_init_mono pa_ext_volume_api_bvolume_init_mono
4625 +#define pa_bvolume_parse_balance pa_ext_volume_api_bvolume_parse_balance
4626 #define pa_bvolume_equal pa_ext_volume_api_bvolume_equal
4627 #define pa_bvolume_from_cvolume pa_ext_volume_api_bvolume_from_cvolume
4628 #define pa_bvolume_to_cvolume pa_ext_volume_api_bvolume_to_cvolume
4629 #define pa_bvolume_copy_balance pa_ext_volume_api_bvolume_copy_balance
4630 #define pa_bvolume_reset_balance pa_ext_volume_api_bvolume_reset_balance
4631 #define pa_bvolume_remap pa_ext_volume_api_bvolume_remap
4632 +#define pa_bvolume_balance_to_string pa_ext_volume_api_bvolume_balance_to_string
4634 #define PA_BVOLUME_SNPRINT_BALANCE_MAX PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX
4635 #define pa_bvolume_snprint_balance pa_ext_volume_api_bvolume_snprint_balance
4636 diff --git a/src/modules/volume-api/device-creator.c b/src/modules/volume-api/device-creator.c
4637 index f35fab0..fc486f8 100644
4638 --- a/src/modules/volume-api/device-creator.c
4639 +++ b/src/modules/volume-api/device-creator.c
4640 @@ -59,6 +59,8 @@ struct device_volume_control {
4641 pa_hook_slot *volume_changed_slot;
4644 +static void device_volume_control_free(struct device_volume_control *control);
4646 struct device_mute_control {
4647 struct device *device;
4648 pa_mute_control *mute_control;
4649 @@ -68,6 +70,8 @@ struct device_mute_control {
4650 pa_hook_slot *mute_changed_slot;
4653 +static void device_mute_control_free(struct device_mute_control *control);
4656 pa_device_creator *creator;
4657 enum device_type type;
4658 @@ -85,6 +89,8 @@ struct device {
4659 struct device *monitor;
4662 +static void device_free(struct device *device);
4664 static const char *device_type_from_icon_name(const char *icon_name) {
4667 @@ -168,112 +174,6 @@ static const char *get_source_description(pa_source *source) {
4668 return source->name;
4671 -static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *volume, bool set_volume, bool set_balance) {
4672 - struct device_volume_control *control;
4673 - struct device *device;
4674 - pa_bvolume bvolume;
4675 - pa_cvolume cvolume;
4678 - pa_assert(volume);
4680 - control = c->userdata;
4681 - device = control->device;
4683 - switch (device->type) {
4684 - case DEVICE_TYPE_PORT:
4685 - if (device->port->direction == PA_DIRECTION_OUTPUT)
4686 - pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4688 - pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4691 - case DEVICE_TYPE_SINK:
4692 - pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4695 - case DEVICE_TYPE_PORT_MONITOR:
4696 - case DEVICE_TYPE_SOURCE:
4697 - pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4702 - bvolume.volume = volume->volume;
4705 - pa_bvolume_copy_balance(&bvolume, volume);
4707 - pa_bvolume_to_cvolume(&bvolume, &cvolume);
4709 - switch (device->type) {
4710 - case DEVICE_TYPE_PORT:
4711 - if (device->port->direction == PA_DIRECTION_OUTPUT)
4712 - pa_sink_set_volume(device->sink, &cvolume, true, true);
4714 - pa_source_set_volume(device->source, &cvolume, true, true);
4717 - case DEVICE_TYPE_PORT_MONITOR:
4718 - case DEVICE_TYPE_SOURCE:
4719 - pa_source_set_volume(device->source, &cvolume, true, true);
4722 - case DEVICE_TYPE_SINK:
4723 - pa_sink_set_volume(device->sink, &cvolume, true, true);
4730 -static struct device_volume_control *device_volume_control_new(struct device *device) {
4731 - struct device_volume_control *control;
4732 - const char *name = NULL;
4733 - bool convertible_to_dB = false;
4734 - bool channel_map_is_writable;
4736 - pa_assert(device);
4738 - control = pa_xnew0(struct device_volume_control, 1);
4739 - control->device = device;
4741 - switch (device->type) {
4742 - case DEVICE_TYPE_PORT:
4743 - name = "port-volume-control";
4745 - if (device->port->direction == PA_DIRECTION_OUTPUT)
4746 - convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4748 - convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4752 - case DEVICE_TYPE_PORT_MONITOR:
4753 - name = "port-monitor-volume-control";
4754 - convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4757 - case DEVICE_TYPE_SINK:
4758 - name = "sink-volume-control";
4759 - convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4762 - case DEVICE_TYPE_SOURCE:
4763 - name = "source-volume-control";
4764 - convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4768 - channel_map_is_writable = false;
4769 - control->volume_control = pa_volume_control_new(device->creator->volume_api, name, device->device->description,
4770 - convertible_to_dB, channel_map_is_writable);
4771 - control->volume_control->set_volume = volume_control_set_volume_cb;
4772 - control->volume_control->userdata = control;
4777 static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *call_data, void *userdata) {
4778 struct device_volume_control *control = userdata;
4779 struct device *device;
4780 @@ -309,24 +209,55 @@ static pa_hook_result_t sink_or_source_volume_changed_cb(void *hook_data, void *
4783 pa_bvolume_from_cvolume(&bvolume, &sink->reference_volume, &sink->channel_map);
4786 pa_bvolume_from_cvolume(&bvolume, &source->reference_volume, &source->channel_map);
4788 + pa_assert_not_reached();
4790 - pa_volume_control_volume_changed(control->volume_control, &bvolume, true, true);
4791 + pa_volume_control_set_volume(control->volume_control, &bvolume, true, true);
4796 -static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
4797 +static int volume_control_set_volume_cb(pa_volume_control *c, const pa_bvolume *original_volume,
4798 + const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
4799 struct device_volume_control *control;
4800 struct device *device;
4801 + pa_bvolume bvolume;
4805 + pa_assert(original_volume);
4806 + pa_assert(remapped_volume);
4808 control = c->userdata;
4809 device = control->device;
4810 - pa_bvolume_to_cvolume(&control->volume_control->volume, &cvolume);
4812 + switch (device->type) {
4813 + case DEVICE_TYPE_PORT:
4814 + if (device->port->direction == PA_DIRECTION_OUTPUT)
4815 + pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4817 + pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4820 + case DEVICE_TYPE_SINK:
4821 + pa_bvolume_from_cvolume(&bvolume, &device->sink->reference_volume, &device->sink->channel_map);
4824 + case DEVICE_TYPE_PORT_MONITOR:
4825 + case DEVICE_TYPE_SOURCE:
4826 + pa_bvolume_from_cvolume(&bvolume, &device->source->reference_volume, &device->source->channel_map);
4831 + bvolume.volume = remapped_volume->volume;
4834 + pa_bvolume_copy_balance(&bvolume, remapped_volume);
4836 + pa_bvolume_to_cvolume(&bvolume, &cvolume);
4838 switch (device->type) {
4839 case DEVICE_TYPE_PORT:
4840 @@ -345,44 +276,91 @@ static void volume_control_set_initial_volume_cb(pa_volume_control *c) {
4841 pa_sink_set_volume(device->sink, &cvolume, true, true);
4848 -static void device_volume_control_put(struct device_volume_control *control) {
4849 - struct device *device;
4850 +static int device_volume_control_new(struct device *device, struct device_volume_control **_r) {
4851 + struct device_volume_control *control = NULL;
4852 + const char *name = NULL;
4854 + bool convertible_to_dB = false;
4857 - pa_assert(control);
4858 + pa_assert(device);
4861 - device = control->device;
4862 + control = pa_xnew0(struct device_volume_control, 1);
4863 + control->device = device;
4865 switch (device->type) {
4866 case DEVICE_TYPE_PORT:
4867 + name = "port-volume-control";
4869 if (device->port->direction == PA_DIRECTION_OUTPUT) {
4870 control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
4871 PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4872 pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
4873 + convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4875 control->volume_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
4876 PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4877 pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
4878 + convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4883 case DEVICE_TYPE_PORT_MONITOR:
4884 - case DEVICE_TYPE_SOURCE:
4885 + name = "port-monitor-volume-control";
4886 control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
4887 PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4888 pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
4889 + convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4892 case DEVICE_TYPE_SINK:
4893 + name = "sink-volume-control";
4894 control->volume_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
4895 PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4896 pa_bvolume_from_cvolume(&volume, &device->sink->reference_volume, &device->sink->channel_map);
4897 + convertible_to_dB = device->sink->flags & PA_SINK_DECIBEL_VOLUME;
4900 + case DEVICE_TYPE_SOURCE:
4901 + name = "source-volume-control";
4902 + control->volume_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
4903 + PA_HOOK_NORMAL, sink_or_source_volume_changed_cb, control);
4904 + pa_bvolume_from_cvolume(&volume, &device->source->reference_volume, &device->source->channel_map);
4905 + convertible_to_dB = device->source->flags & PA_SOURCE_DECIBEL_VOLUME;
4909 - pa_volume_control_put(control->volume_control, &volume, volume_control_set_initial_volume_cb);
4910 + r = pa_volume_control_new(device->creator->volume_api, name, false, &control->volume_control);
4914 + pa_volume_control_set_description(control->volume_control, device->device->description);
4915 + pa_volume_control_set_channel_map(control->volume_control, &volume.channel_map);
4916 + pa_volume_control_set_volume(control->volume_control, &volume, true, true);
4917 + pa_volume_control_set_convertible_to_dB(control->volume_control, convertible_to_dB);
4918 + control->volume_control->set_volume = volume_control_set_volume_cb;
4919 + control->volume_control->userdata = control;
4926 + device_volume_control_free(control);
4931 +static void device_volume_control_put(struct device_volume_control *control) {
4932 + pa_assert(control);
4934 + pa_volume_control_put(control->volume_control);
4937 static void device_volume_control_unlink(struct device_volume_control *control) {
4938 @@ -395,11 +373,6 @@ static void device_volume_control_unlink(struct device_volume_control *control)
4940 if (control->volume_control)
4941 pa_volume_control_unlink(control->volume_control);
4943 - if (control->volume_changed_slot) {
4944 - pa_hook_slot_free(control->volume_changed_slot);
4945 - control->volume_changed_slot = NULL;
4949 static void device_volume_control_free(struct device_volume_control *control) {
4950 @@ -411,71 +384,10 @@ static void device_volume_control_free(struct device_volume_control *control) {
4951 if (control->volume_control)
4952 pa_volume_control_free(control->volume_control);
4954 - pa_xfree(control);
4957 -static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
4958 - struct device_mute_control *control;
4959 - struct device *device;
4963 - control = c->userdata;
4964 - device = control->device;
4966 - switch (device->type) {
4967 - case DEVICE_TYPE_PORT:
4968 - if (device->port->direction == PA_DIRECTION_OUTPUT)
4969 - pa_sink_set_mute(device->sink, mute, true);
4971 - pa_source_set_mute(device->source, mute, true);
4974 - case DEVICE_TYPE_PORT_MONITOR:
4975 - case DEVICE_TYPE_SOURCE:
4976 - pa_source_set_mute(device->source, mute, true);
4979 - case DEVICE_TYPE_SINK:
4980 - pa_sink_set_mute(device->sink, mute, true);
4987 -static struct device_mute_control *device_mute_control_new(struct device *device) {
4988 - struct device_mute_control *control;
4989 - const char *name = NULL;
4991 - pa_assert(device);
4993 - control = pa_xnew0(struct device_mute_control, 1);
4994 - control->device = device;
4996 - switch (device->type) {
4997 - case DEVICE_TYPE_PORT:
4998 - name = "port-mute-control";
5001 - case DEVICE_TYPE_PORT_MONITOR:
5002 - name = "port-monitor-mute-control";
5005 - case DEVICE_TYPE_SINK:
5006 - name = "sink-mute-control";
5009 - case DEVICE_TYPE_SOURCE:
5010 - name = "source-mute-control";
5014 - control->mute_control = pa_mute_control_new(device->creator->volume_api, name, device->device->description);
5015 - control->mute_control->set_mute = mute_control_set_mute_cb;
5016 - control->mute_control->userdata = control;
5017 + if (control->volume_changed_slot)
5018 + pa_hook_slot_free(control->volume_changed_slot);
5021 + pa_xfree(control);
5024 static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
5025 @@ -518,13 +430,13 @@ static pa_hook_result_t sink_or_source_mute_changed_cb(void *hook_data, void *ca
5027 pa_assert_not_reached();
5029 - pa_mute_control_mute_changed(control->mute_control, mute);
5030 + pa_mute_control_set_mute(control->mute_control, mute);
5035 -static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
5036 - struct device_volume_control *control;
5037 +static int mute_control_set_mute_cb(pa_mute_control *c, bool mute) {
5038 + struct device_mute_control *control;
5039 struct device *device;
5042 @@ -535,32 +447,40 @@ static void mute_control_set_initial_mute_cb(pa_mute_control *c) {
5043 switch (device->type) {
5044 case DEVICE_TYPE_PORT:
5045 if (device->port->direction == PA_DIRECTION_OUTPUT)
5046 - pa_sink_set_mute(device->sink, c->mute, true);
5047 + pa_sink_set_mute(device->sink, mute, true);
5049 - pa_source_set_mute(device->source, c->mute, true);
5050 + pa_source_set_mute(device->source, mute, true);
5053 case DEVICE_TYPE_PORT_MONITOR:
5054 case DEVICE_TYPE_SOURCE:
5055 - pa_source_set_mute(device->source, c->mute, true);
5056 + pa_source_set_mute(device->source, mute, true);
5059 case DEVICE_TYPE_SINK:
5060 - pa_sink_set_mute(device->sink, c->mute, true);
5061 + pa_sink_set_mute(device->sink, mute, true);
5068 -static void device_mute_control_put(struct device_mute_control *control) {
5069 - struct device *device;
5070 +static int device_mute_control_new(struct device *device, struct device_mute_control **_r) {
5071 + struct device_mute_control *control = NULL;
5072 + const char *name = NULL;
5076 - pa_assert(control);
5077 + pa_assert(device);
5080 - device = control->device;
5081 + control = pa_xnew0(struct device_mute_control, 1);
5082 + control->device = device;
5084 switch (device->type) {
5085 case DEVICE_TYPE_PORT:
5086 + name = "port-mute-control";
5088 if (device->port->direction == PA_DIRECTION_OUTPUT) {
5089 control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
5090 PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5091 @@ -573,20 +493,50 @@ static void device_mute_control_put(struct device_mute_control *control) {
5094 case DEVICE_TYPE_PORT_MONITOR:
5095 - case DEVICE_TYPE_SOURCE:
5096 - control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
5097 + name = "port-monitor-mute-control";
5098 + control->mute_changed_slot = pa_hook_connect(&device->port->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
5099 PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5100 mute = device->source->muted;
5103 case DEVICE_TYPE_SINK:
5104 + name = "sink-mute-control";
5105 control->mute_changed_slot = pa_hook_connect(&device->sink->core->hooks[PA_CORE_HOOK_SINK_MUTE_CHANGED],
5106 PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5107 mute = device->sink->muted;
5110 + case DEVICE_TYPE_SOURCE:
5111 + name = "source-mute-control";
5112 + control->mute_changed_slot = pa_hook_connect(&device->source->core->hooks[PA_CORE_HOOK_SOURCE_MUTE_CHANGED],
5113 + PA_HOOK_NORMAL, sink_or_source_mute_changed_cb, control);
5114 + mute = device->source->muted;
5118 - pa_mute_control_put(control->mute_control, mute, true, mute_control_set_initial_mute_cb);
5119 + r = pa_mute_control_new(device->creator->volume_api, name, false, &control->mute_control);
5123 + pa_mute_control_set_description(control->mute_control, device->device->description);
5124 + pa_mute_control_set_mute(control->mute_control, mute);
5125 + control->mute_control->set_mute = mute_control_set_mute_cb;
5126 + control->mute_control->userdata = control;
5133 + device_mute_control_free(control);
5138 +static void device_mute_control_put(struct device_mute_control *control) {
5139 + pa_assert(control);
5141 + pa_mute_control_put(control->mute_control);
5144 static void device_mute_control_unlink(struct device_mute_control *control) {
5145 @@ -599,11 +549,6 @@ static void device_mute_control_unlink(struct device_mute_control *control) {
5147 if (control->mute_control)
5148 pa_mute_control_unlink(control->mute_control);
5150 - if (control->mute_changed_slot) {
5151 - pa_hook_slot_free(control->mute_changed_slot);
5152 - control->mute_changed_slot = NULL;
5156 static void device_mute_control_free(struct device_mute_control *control) {
5157 @@ -615,6 +560,9 @@ static void device_mute_control_free(struct device_mute_control *control) {
5158 if (control->mute_control)
5159 pa_mute_control_free(control->mute_control);
5161 + if (control->mute_changed_slot)
5162 + pa_hook_slot_free(control->mute_changed_slot);
5167 @@ -707,22 +655,24 @@ static pa_hook_result_t sink_or_source_proplist_changed_cb(void *hook_data, void
5170 pa_device_description_changed(device->device, description);
5171 - pa_volume_control_description_changed(device->volume_control->volume_control, description);
5172 - pa_mute_control_description_changed(device->mute_control->mute_control, description);
5173 + pa_volume_control_set_description(device->volume_control->volume_control, description);
5174 + pa_mute_control_set_description(device->mute_control->mute_control, description);
5179 -static struct device *device_new(pa_device_creator *creator, enum device_type type, void *core_device) {
5180 +static int device_new(pa_device_creator *creator, enum device_type type, void *core_device, struct device **_r) {
5181 struct device *device = NULL;
5182 const char *name = NULL;
5183 char *description = NULL;
5184 pa_direction_t direction = PA_DIRECTION_OUTPUT;
5185 const char *device_type = NULL;
5186 bool create_volume_and_mute_controls = true;
5190 pa_assert(core_device);
5193 device = pa_xnew0(struct device, 1);
5194 device->creator = creator;
5195 @@ -767,18 +717,20 @@ static struct device *device_new(pa_device_creator *creator, enum device_type ty
5199 - device->device = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0);
5200 + r = pa_device_new(creator->volume_api, name, description, direction, &device_type, device_type ? 1 : 0, &device->device);
5201 pa_xfree(description);
5205 if (create_volume_and_mute_controls) {
5206 - device->volume_control = device_volume_control_new(device);
5207 - device->mute_control = device_mute_control_new(device);
5208 + device_volume_control_new(device, &device->volume_control);
5209 + device_mute_control_new(device, &device->mute_control);
5213 case DEVICE_TYPE_PORT:
5214 if (device->port->direction == PA_DIRECTION_OUTPUT)
5215 - device->monitor = device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port);
5216 + device_new(creator, DEVICE_TYPE_PORT_MONITOR, device->port, &device->monitor);
5219 case DEVICE_TYPE_PORT_MONITOR:
5220 @@ -795,7 +747,14 @@ static struct device *device_new(pa_device_creator *creator, enum device_type ty
5230 + device_free(device);
5235 static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data, void *userdata) {
5236 @@ -825,25 +784,36 @@ static pa_hook_result_t port_active_changed_cb(void *hook_data, void *call_data,
5237 pa_assert_not_reached();
5240 - if (should_have_volume_and_mute_controls && !device->volume_control) {
5241 - pa_assert(!device->mute_control);
5242 + if (should_have_volume_and_mute_controls) {
5245 - device->volume_control = device_volume_control_new(device);
5246 - device_volume_control_put(device->volume_control);
5247 - pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
5248 + if (!device->volume_control) {
5249 + r = device_volume_control_new(device, &device->volume_control);
5251 + device_volume_control_put(device->volume_control);
5252 + pa_device_set_default_volume_control(device->device, device->volume_control->volume_control);
5256 - device->mute_control = device_mute_control_new(device);
5257 - device_mute_control_put(device->mute_control);
5258 - pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
5259 + if (!device->mute_control) {
5260 + r = device_mute_control_new(device, &device->mute_control);
5262 + device_mute_control_put(device->mute_control);
5263 + pa_device_set_default_mute_control(device->device, device->mute_control->mute_control);
5268 - if (!should_have_volume_and_mute_controls && device->volume_control) {
5269 - pa_assert(device->mute_control);
5270 + if (!should_have_volume_and_mute_controls) {
5271 + if (device->mute_control) {
5272 + device_mute_control_free(device->mute_control);
5273 + device->mute_control = NULL;
5276 - device_mute_control_free(device->mute_control);
5277 - device->mute_control = NULL;
5278 - device_volume_control_free(device->volume_control);
5279 - device->volume_control = NULL;
5280 + if (device->volume_control) {
5281 + device_volume_control_free(device->volume_control);
5282 + device->volume_control = NULL;
5287 @@ -928,6 +898,7 @@ static void device_free(struct device *device) {
5289 static void create_device(pa_device_creator *creator, enum device_type type, void *core_device) {
5290 struct device *device;
5294 pa_assert(core_device);
5295 @@ -956,9 +927,11 @@ static void create_device(pa_device_creator *creator, enum device_type type, voi
5299 - device = device_new(creator, type, core_device);
5300 - pa_hashmap_put(creator->devices, core_device, device);
5301 - device_put(device);
5302 + r = device_new(creator, type, core_device, &device);
5304 + pa_hashmap_put(creator->devices, core_device, device);
5305 + device_put(device);
5309 static pa_hook_result_t card_put_cb(void *hook_data, void *call_data, void *userdata) {
5310 diff --git a/src/modules/volume-api/device.c b/src/modules/volume-api/device.c
5311 index ea496ba..c1a580c 100644
5312 --- a/src/modules/volume-api/device.c
5313 +++ b/src/modules/volume-api/device.c
5316 #include <pulsecore/core-util.h>
5318 -pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5319 - const char * const *device_types, unsigned n_device_types) {
5320 - pa_device *device;
5321 +int pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5322 + const char * const *device_types, unsigned n_device_types, pa_device **_r) {
5323 + pa_device *device = NULL;
5329 pa_assert(description);
5330 pa_assert(device_types || n_device_types == 0);
5333 device = pa_xnew0(pa_device, 1);
5334 device->volume_api = api;
5335 device->index = pa_volume_api_allocate_device_index(api);
5336 - pa_assert_se(pa_volume_api_register_name(api, name, false, &device->name) >= 0);
5338 + r = pa_volume_api_register_name(api, name, false, &device->name);
5342 device->description = pa_xstrdup(description);
5343 device->direction = direction;
5344 device->device_types = pa_dynarray_new(pa_xfree);
5345 @@ -57,7 +63,14 @@ pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *descr
5346 device->use_default_volume_control = true;
5347 device->use_default_mute_control = true;
5355 + pa_device_free(device);
5360 void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control) {
5361 @@ -84,7 +97,6 @@ void pa_device_put(pa_device *device, pa_volume_control *default_volume_control,
5364 pa_volume_api_add_device(device->volume_api, device);
5366 device->linked = true;
5368 device_types_str = pa_join((const char * const *) pa_dynarray_get_raw_array(device->device_types),
5369 @@ -120,35 +132,21 @@ void pa_device_unlink(pa_device *device) {
5370 pa_log_debug("Unlinking device %s.", device->name);
5373 - pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
5375 - pa_volume_api_remove_device(device->volume_api, device);
5376 + pa_volume_api_remove_device(device->volume_api, device);
5378 - if (device->mute_control) {
5379 - pa_mute_control_remove_device(device->mute_control, device);
5380 - device->mute_control = NULL;
5383 - if (device->default_mute_control) {
5384 - pa_mute_control_remove_default_for_device(device->default_mute_control, device);
5385 - device->default_mute_control = NULL;
5387 + pa_hook_fire(&device->volume_api->hooks[PA_VOLUME_API_HOOK_DEVICE_UNLINK], device);
5389 - if (device->volume_control) {
5390 - pa_volume_control_remove_device(device->volume_control, device);
5391 - device->volume_control = NULL;
5394 - if (device->default_volume_control) {
5395 - pa_volume_control_remove_default_for_device(device->default_volume_control, device);
5396 - device->default_volume_control = NULL;
5398 + pa_device_set_mute_control(device, NULL);
5399 + pa_device_set_default_mute_control(device, NULL);
5400 + pa_device_set_volume_control(device, NULL);
5401 + pa_device_set_default_volume_control(device, NULL);
5404 void pa_device_free(pa_device *device) {
5407 - if (!device->unlinked)
5408 + /* unlink() expects name to be set. */
5409 + if (!device->unlinked && device->name)
5410 pa_device_unlink(device);
5412 if (device->proplist)
5413 diff --git a/src/modules/volume-api/device.h b/src/modules/volume-api/device.h
5414 index 9eac7e9..8bd5158 100644
5415 --- a/src/modules/volume-api/device.h
5416 +++ b/src/modules/volume-api/device.h
5417 @@ -51,8 +51,8 @@ struct pa_device {
5421 -pa_device *pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5422 - const char * const *device_types, unsigned n_device_types);
5423 +int pa_device_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction,
5424 + const char * const *device_types, unsigned n_device_types, pa_device **_r);
5425 void pa_device_put(pa_device *device, pa_volume_control *default_volume_control, pa_mute_control *default_mute_control);
5426 void pa_device_unlink(pa_device *device);
5427 void pa_device_free(pa_device *device);
5428 diff --git a/src/modules/volume-api/inidb.c b/src/modules/volume-api/inidb.c
5429 new file mode 100644
5430 index 0000000..8116e72
5432 +++ b/src/modules/volume-api/inidb.c
5435 + This file is part of PulseAudio.
5437 + Copyright 2014 Intel Corporation
5439 + PulseAudio is free software; you can redistribute it and/or modify
5440 + it under the terms of the GNU Lesser General Public License as published
5441 + by the Free Software Foundation; either version 2.1 of the License,
5442 + or (at your option) any later version.
5444 + PulseAudio is distributed in the hope that it will be useful, but
5445 + WITHOUT ANY WARRANTY; without even the implied warranty of
5446 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5447 + General Public License for more details.
5449 + You should have received a copy of the GNU Lesser General Public License
5450 + along with PulseAudio; if not, write to the Free Software
5451 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
5455 +#ifdef HAVE_CONFIG_H
5456 +#include <config.h>
5461 +#include <pulse/mainloop-api.h>
5462 +#include <pulse/rtclock.h>
5463 +#include <pulse/timeval.h>
5464 +#include <pulse/xmalloc.h>
5466 +#include <pulsecore/conf-parser.h>
5467 +#include <pulsecore/core-error.h>
5468 +#include <pulsecore/core-rtclock.h>
5469 +#include <pulsecore/core-util.h>
5470 +#include <pulsecore/hashmap.h>
5471 +#include <pulsecore/macro.h>
5472 +#include <pulsecore/namereg.h>
5476 +#define SAVE_INTERVAL_USEC (10 * PA_USEC_PER_SEC)
5482 + char *tmp_file_path;
5483 + pa_hashmap *tables; /* table name -> pa_inidb_table */
5484 + pa_time_event *time_event;
5489 +struct pa_inidb_table {
5492 + pa_hashmap *columns; /* column name -> column */
5493 + pa_hashmap *rows; /* row id -> pa_inidb_row */
5494 + pa_inidb_get_object_cb_t get_object;
5499 + pa_inidb_parse_cb_t parse;
5502 +struct pa_inidb_row {
5505 + pa_hashmap *cells; /* column name -> cell */
5508 +struct pa_inidb_cell {
5510 + struct column *column;
5515 +static void save(pa_inidb *db);
5517 +static pa_inidb_table *table_new(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb);
5518 +static void table_free(pa_inidb_table *table);
5519 +static pa_inidb_row *table_add_row_internal(pa_inidb_table *table, const char *row_id);
5521 +static struct column *column_new(const char *name, pa_inidb_parse_cb_t parse_cb);
5522 +static void column_free(struct column *column);
5524 +static pa_inidb_row *row_new(pa_inidb_table *table, const char *id);
5525 +static void row_free(pa_inidb_row *row);
5527 +static pa_inidb_cell *cell_new(pa_inidb *db, struct column *column);
5528 +static void cell_free(pa_inidb_cell *cell);
5529 +static void cell_set_value_internal(pa_inidb_cell *cell, const char *value);
5531 +static int parse_assignment(pa_config_parser_state *state) {
5533 + const char *end_of_table_name;
5534 + size_t table_name_len;
5536 + pa_inidb_table *table;
5537 + const char *row_id;
5538 + pa_inidb_row *row;
5541 + struct column *column;
5542 + pa_inidb_cell *cell;
5546 + db = state->userdata;
5548 + /* FIXME: pa_config_parser should be improved so that it could parse the
5549 + * table name and row id for us in the section header. */
5550 + end_of_table_name = strchr(state->section, ' ');
5551 + if (!end_of_table_name) {
5552 + pa_log("[%s:%u] Failed to parse table name and row id in section \"%s\"", state->filename, state->lineno,
5554 + return -PA_ERR_INVALID;
5557 + table_name_len = end_of_table_name - state->section;
5558 + table_name = pa_xstrndup(state->section, table_name_len);
5559 + table = pa_hashmap_get(db->tables, table_name);
5561 + pa_log("[%s:%u] Unknown table name: \"%s\"", state->filename, state->lineno, table_name);
5562 + pa_xfree(table_name);
5564 + return -PA_ERR_INVALID;
5566 + row_id = end_of_table_name + 1;
5567 + if (!pa_namereg_is_valid_name(row_id)) {
5568 + pa_log("[%s:%u] Invalid row id: \"%s\"", state->filename, state->lineno, row_id);
5569 + return -PA_ERR_INVALID;
5572 + /* This is not strictly necessary, but we do this to avoid saving the
5573 + * database when there is no actual change. Without this, the get_object()
5574 + * callback would cause redundant saving whenever creating new objects. */
5575 + if (!(row = pa_hashmap_get(table->rows, row_id)))
5576 + row = table_add_row_internal(table, row_id);
5578 + r = table->get_object(db, row_id, &object);
5580 + pa_log("[%s:%u] Failed to create object %s.", state->filename, state->lineno, row_id);
5584 + column = pa_hashmap_get(table->columns, state->lvalue);
5586 + pa_log("[%s:%u] Unknown column name: \"%s\"", state->filename, state->lineno, state->lvalue);
5587 + return -PA_ERR_INVALID;
5590 + /* This is not strictly necessary, but we do this to avoid saving the
5591 + * database when there is no actual change. Without this, the parse()
5592 + * callback would cause redundant saving whenever setting the cell value
5593 + * for the first time. */
5594 + cell = pa_hashmap_get(row->cells, column->name);
5595 + cell_set_value_internal(cell, state->rvalue);
5597 + r = column->parse(db, state->rvalue, object);
5599 + pa_log("[%s:%u] Failed to parse %s value \"%s\".", state->filename, state->lineno, column->name, state->rvalue);
5606 +pa_inidb *pa_inidb_new(pa_core *core, const char *name, void *userdata) {
5613 + db = pa_xnew0(pa_inidb, 1);
5615 + db->name = pa_xstrdup(name);
5617 + r = pa_append_to_config_home_dir(name, true, &db->file_path);
5619 + pa_log("Failed to find the file location for database \"%s\". The database will start empty, and updates will not be "
5620 + "saved on disk.", name);
5621 + db->failed = true;
5624 + if (db->file_path)
5625 + db->tmp_file_path = pa_sprintf_malloc("%s.tmp", db->file_path);
5627 + db->tables = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5628 + (pa_free_cb_t) table_free);
5629 + db->userdata = userdata;
5634 +void pa_inidb_free(pa_inidb *db) {
5637 + if (db->time_event) {
5638 + db->core->mainloop->time_free(db->time_event);
5643 + pa_hashmap_free(db->tables);
5645 + pa_xfree(db->tmp_file_path);
5646 + pa_xfree(db->file_path);
5647 + pa_xfree(db->name);
5651 +void *pa_inidb_get_userdata(pa_inidb *db) {
5654 + return db->userdata;
5657 +pa_inidb_table *pa_inidb_add_table(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb) {
5658 + pa_inidb_table *table;
5662 + pa_assert(get_object_cb);
5664 + table = table_new(db, name, get_object_cb);
5665 + pa_assert_se(pa_hashmap_put(db->tables, table->name, table) >= 0);
5670 +void pa_inidb_load(pa_inidb *db) {
5671 + unsigned n_config_items;
5672 + pa_inidb_table *table;
5674 + pa_config_item *config_items;
5682 + n_config_items = 0;
5683 + PA_HASHMAP_FOREACH(table, db->tables, state)
5684 + n_config_items += pa_hashmap_size(table->columns);
5686 + config_items = pa_xnew0(pa_config_item, n_config_items + 1);
5689 + PA_HASHMAP_FOREACH(table, db->tables, state) {
5690 + struct column *column;
5693 + PA_HASHMAP_FOREACH(column, table->columns, state2) {
5694 + config_items[i].lvalue = column->name;
5695 + config_items[i].parse = parse_assignment;
5700 + pa_config_parse(db->file_path, NULL, config_items, NULL, db);
5701 + pa_xfree(config_items);
5704 +static void save(pa_inidb *db) {
5706 + pa_inidb_table *table;
5715 + f = pa_fopen_cloexec(db->tmp_file_path, "w");
5717 + pa_log("pa_fopen_cloexec() failed: %s", pa_cstrerror(errno));
5721 + PA_HASHMAP_FOREACH(table, db->tables, state) {
5722 + pa_inidb_row *row;
5725 + PA_HASHMAP_FOREACH(row, table->rows, state2) {
5727 + size_t items_written;
5728 + pa_inidb_cell *cell;
5731 + len = strlen(row->header);
5732 + items_written = fwrite(row->header, len, 1, f);
5734 + if (items_written != 1) {
5735 + pa_log("fwrite() failed: %s", pa_cstrerror(errno));
5739 + PA_HASHMAP_FOREACH(cell, row->cells, state3) {
5740 + if (!cell->assignment)
5743 + len = strlen(cell->assignment);
5744 + items_written = fwrite(cell->assignment, len, 1, f);
5746 + if (items_written != 1) {
5747 + pa_log("fwrite() failed: %s", pa_cstrerror(errno));
5752 + items_written = fwrite("\n", 1, 1, f);
5754 + if (items_written != 1) {
5755 + pa_log("fwrite() failed: %s", pa_cstrerror(errno));
5763 + pa_log("fclose() failed: %s", pa_cstrerror(errno));
5767 + r = rename(db->tmp_file_path, db->file_path);
5769 + pa_log("rename() failed: %s", pa_cstrerror(errno));
5773 + pa_log_debug("Database \"%s\" saved.", db->name);
5781 + db->failed = true;
5782 + pa_log("Saving database \"%s\" failed, current and future database changes will not be written to the disk.", db->name);
5785 +static pa_inidb_table *table_new(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb) {
5786 + pa_inidb_table *table;
5790 + pa_assert(get_object_cb);
5792 + table = pa_xnew0(pa_inidb_table, 1);
5794 + table->name = pa_xstrdup(name);
5795 + table->columns = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5796 + (pa_free_cb_t) column_free);
5797 + table->rows = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5798 + (pa_free_cb_t) row_free);
5799 + table->get_object = get_object_cb;
5804 +static void table_free(pa_inidb_table *table) {
5808 + pa_hashmap_free(table->rows);
5810 + if (table->columns)
5811 + pa_hashmap_free(table->columns);
5813 + pa_xfree(table->name);
5817 +void pa_inidb_table_add_column(pa_inidb_table *table, const char *name, pa_inidb_parse_cb_t parse_cb) {
5818 + struct column *column;
5822 + pa_assert(parse_cb);
5824 + column = column_new(name, parse_cb);
5825 + pa_assert_se(pa_hashmap_put(table->columns, column->name, column) >= 0);
5828 +static pa_inidb_row *table_add_row_internal(pa_inidb_table *table, const char *row_id) {
5829 + pa_inidb_row *row;
5832 + pa_assert(row_id);
5834 + row = row_new(table, row_id);
5835 + pa_assert_se(pa_hashmap_put(table->rows, row->id, row) >= 0);
5840 +static void time_cb(pa_mainloop_api *api, pa_time_event *event, const struct timeval *tv, void *userdata) {
5841 + pa_inidb *db = userdata;
5846 + api->time_free(event);
5847 + db->time_event = NULL;
5852 +static void trigger_save(pa_inidb *db) {
5853 + struct timeval tv;
5857 + if (db->time_event)
5860 + pa_timeval_rtstore(&tv, pa_rtclock_now() + SAVE_INTERVAL_USEC, true);
5861 + db->time_event = db->core->mainloop->time_new(db->core->mainloop, &tv, time_cb, db);
5864 +pa_inidb_row *pa_inidb_table_add_row(pa_inidb_table *table, const char *row_id) {
5865 + pa_inidb_row *row;
5868 + pa_assert(row_id);
5870 + row = pa_hashmap_get(table->rows, row_id);
5874 + row = table_add_row_internal(table, row_id);
5875 + trigger_save(table->db);
5880 +static struct column *column_new(const char *name, pa_inidb_parse_cb_t parse_cb) {
5881 + struct column *column;
5884 + pa_assert(parse_cb);
5886 + column = pa_xnew(struct column, 1);
5887 + column->name = pa_xstrdup(name);
5888 + column->parse = parse_cb;
5893 +static void column_free(struct column *column) {
5894 + pa_assert(column);
5896 + pa_xfree(column->name);
5900 +static pa_inidb_row *row_new(pa_inidb_table *table, const char *id) {
5901 + pa_inidb_row *row;
5902 + struct column *column;
5908 + row = pa_xnew0(pa_inidb_row, 1);
5909 + row->id = pa_xstrdup(id);
5910 + row->header = pa_sprintf_malloc("[%s %s]\n", table->name, id);
5911 + row->cells = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
5912 + (pa_free_cb_t) cell_free);
5914 + PA_HASHMAP_FOREACH(column, table->columns, state) {
5915 + pa_inidb_cell *cell;
5917 + cell = cell_new(table->db, column);
5918 + pa_hashmap_put(row->cells, cell->column->name, cell);
5924 +static void row_free(pa_inidb_row *row) {
5927 + pa_xfree(row->header);
5928 + pa_xfree(row->id);
5932 +pa_inidb_cell *pa_inidb_row_get_cell(pa_inidb_row *row, const char *column_name) {
5933 + pa_inidb_cell *cell;
5936 + pa_assert(column_name);
5938 + pa_assert_se(cell = pa_hashmap_get(row->cells, column_name));
5943 +static pa_inidb_cell *cell_new(pa_inidb *db, struct column *column) {
5944 + pa_inidb_cell *cell;
5947 + pa_assert(column);
5949 + cell = pa_xnew0(pa_inidb_cell, 1);
5951 + cell->column = column;
5956 +static void cell_free(pa_inidb_cell *cell) {
5959 + pa_xfree(cell->assignment);
5960 + pa_xfree(cell->value);
5964 +static void cell_set_value_internal(pa_inidb_cell *cell, const char *value) {
5968 + pa_xfree(cell->value);
5969 + cell->value = pa_xstrdup(value);
5971 + pa_xfree(cell->assignment);
5973 + cell->assignment = pa_sprintf_malloc("%s = %s\n", cell->column->name, value);
5975 + cell->assignment = NULL;
5978 +void pa_inidb_cell_set_value(pa_inidb_cell *cell, const char *value) {
5981 + if (pa_safe_streq(value, cell->value))
5984 + cell_set_value_internal(cell, value);
5985 + trigger_save(cell->db);
5987 diff --git a/src/modules/volume-api/inidb.h b/src/modules/volume-api/inidb.h
5988 new file mode 100644
5989 index 0000000..ded73ba
5991 +++ b/src/modules/volume-api/inidb.h
5993 +#ifndef fooinidbhfoo
5994 +#define fooinidbhfoo
5997 + This file is part of PulseAudio.
5999 + Copyright 2014 Intel Corporation
6001 + PulseAudio is free software; you can redistribute it and/or modify
6002 + it under the terms of the GNU Lesser General Public License as published
6003 + by the Free Software Foundation; either version 2.1 of the License,
6004 + or (at your option) any later version.
6006 + PulseAudio is distributed in the hope that it will be useful, but
6007 + WITHOUT ANY WARRANTY; without even the implied warranty of
6008 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6009 + General Public License for more details.
6011 + You should have received a copy of the GNU Lesser General Public License
6012 + along with PulseAudio; if not, write to the Free Software
6013 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
6017 +#include <pulsecore/core.h>
6019 +typedef struct pa_inidb pa_inidb;
6020 +typedef struct pa_inidb_cell pa_inidb_cell;
6021 +typedef struct pa_inidb_row pa_inidb_row;
6022 +typedef struct pa_inidb_table pa_inidb_table;
6024 +/* If there's no object with the given name, the implementation is expected to
6025 + * create a new object (or at least try to). */
6026 +typedef int (*pa_inidb_get_object_cb_t)(pa_inidb *db, const char *name, void **_r);
6028 +/* The implementation is expected to parse the value, and set the parsed value
6029 + * on the object. */
6030 +typedef int (*pa_inidb_parse_cb_t)(pa_inidb *db, const char *value, void *object);
6032 +pa_inidb *pa_inidb_new(pa_core *core, const char *name, void *userdata);
6033 +void pa_inidb_free(pa_inidb *db);
6035 +void *pa_inidb_get_userdata(pa_inidb *db);
6036 +pa_inidb_table *pa_inidb_add_table(pa_inidb *db, const char *name, pa_inidb_get_object_cb_t get_object_cb);
6037 +void pa_inidb_load(pa_inidb *db);
6039 +void pa_inidb_table_add_column(pa_inidb_table *table, const char *name, pa_inidb_parse_cb_t parse_cb);
6040 +pa_inidb_row *pa_inidb_table_add_row(pa_inidb_table *table, const char *row_id);
6042 +pa_inidb_cell *pa_inidb_row_get_cell(pa_inidb_row *row, const char *column_name);
6044 +void pa_inidb_cell_set_value(pa_inidb_cell *cell, const char *value);
6047 diff --git a/src/modules/volume-api/module-volume-api.c b/src/modules/volume-api/module-volume-api.c
6048 index 845ac09..7b112f6 100644
6049 --- a/src/modules/volume-api/module-volume-api.c
6050 +++ b/src/modules/volume-api/module-volume-api.c
6051 @@ -51,6 +51,7 @@ struct userdata {
6052 pa_hook_slot *volume_control_unlink_slot;
6053 pa_hook_slot *volume_control_description_changed_slot;
6054 pa_hook_slot *volume_control_volume_changed_slot;
6055 + pa_hook_slot *volume_control_convertible_to_db_changed_slot;
6056 pa_hook_slot *mute_control_put_slot;
6057 pa_hook_slot *mute_control_unlink_slot;
6058 pa_hook_slot *mute_control_description_changed_slot;
6059 @@ -63,10 +64,13 @@ struct userdata {
6060 pa_hook_slot *stream_put_slot;
6061 pa_hook_slot *stream_unlink_slot;
6062 pa_hook_slot *stream_description_changed_slot;
6063 + pa_hook_slot *stream_proplist_changed_slot;
6064 pa_hook_slot *stream_volume_control_changed_slot;
6065 + pa_hook_slot *stream_relative_volume_control_changed_slot;
6066 pa_hook_slot *stream_mute_control_changed_slot;
6067 pa_hook_slot *audio_group_put_slot;
6068 pa_hook_slot *audio_group_unlink_slot;
6069 + pa_hook_slot *audio_group_description_changed_slot;
6070 pa_hook_slot *audio_group_volume_control_changed_slot;
6071 pa_hook_slot *audio_group_mute_control_changed_slot;
6072 pa_hook_slot *main_output_volume_control_changed_slot;
6073 @@ -1247,6 +1251,9 @@ int pa__init(pa_module *module) {
6074 u->volume_control_volume_changed_slot =
6075 pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED],
6076 PA_HOOK_NORMAL, volume_control_event_cb, u);
6077 + u->volume_control_convertible_to_db_changed_slot =
6078 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED], PA_HOOK_NORMAL,
6079 + volume_control_event_cb, u);
6080 u->mute_control_put_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT],
6081 PA_HOOK_NORMAL, mute_control_put_cb, u);
6082 u->mute_control_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK],
6083 @@ -1277,9 +1284,14 @@ int pa__init(pa_module *module) {
6084 u->stream_description_changed_slot =
6085 pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], PA_HOOK_NORMAL,
6086 stream_event_cb, u);
6087 + u->stream_proplist_changed_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED],
6088 + PA_HOOK_NORMAL, stream_event_cb, u);
6089 u->stream_volume_control_changed_slot =
6090 pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED],
6091 PA_HOOK_NORMAL, stream_event_cb, u);
6092 + u->stream_relative_volume_control_changed_slot =
6093 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED],
6094 + PA_HOOK_NORMAL, stream_event_cb, u);
6095 u->stream_mute_control_changed_slot =
6096 pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], PA_HOOK_NORMAL,
6097 stream_event_cb, u);
6098 @@ -1287,6 +1299,9 @@ int pa__init(pa_module *module) {
6099 PA_HOOK_NORMAL, audio_group_put_cb, u);
6100 u->audio_group_unlink_slot = pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK],
6101 PA_HOOK_NORMAL, audio_group_unlink_cb, u);
6102 + u->audio_group_description_changed_slot =
6103 + pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED], PA_HOOK_NORMAL,
6104 + audio_group_event_cb, u);
6105 u->audio_group_volume_control_changed_slot =
6106 pa_hook_connect(&u->volume_api->hooks[PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED],
6107 PA_HOOK_NORMAL, audio_group_event_cb, u);
6108 @@ -1352,6 +1367,9 @@ void pa__done(pa_module *module) {
6109 if (u->audio_group_volume_control_changed_slot)
6110 pa_hook_slot_free(u->audio_group_volume_control_changed_slot);
6112 + if (u->audio_group_description_changed_slot)
6113 + pa_hook_slot_free(u->audio_group_description_changed_slot);
6115 if (u->audio_group_unlink_slot)
6116 pa_hook_slot_free(u->audio_group_unlink_slot);
6118 @@ -1361,9 +1379,15 @@ void pa__done(pa_module *module) {
6119 if (u->stream_mute_control_changed_slot)
6120 pa_hook_slot_free(u->stream_mute_control_changed_slot);
6122 + if (u->stream_relative_volume_control_changed_slot)
6123 + pa_hook_slot_free(u->stream_relative_volume_control_changed_slot);
6125 if (u->stream_volume_control_changed_slot)
6126 pa_hook_slot_free(u->stream_volume_control_changed_slot);
6128 + if (u->stream_proplist_changed_slot)
6129 + pa_hook_slot_free(u->stream_proplist_changed_slot);
6131 if (u->stream_description_changed_slot)
6132 pa_hook_slot_free(u->stream_description_changed_slot);
6134 @@ -1400,6 +1424,9 @@ void pa__done(pa_module *module) {
6135 if (u->mute_control_put_slot)
6136 pa_hook_slot_free(u->mute_control_put_slot);
6138 + if (u->volume_control_convertible_to_db_changed_slot)
6139 + pa_hook_slot_free(u->volume_control_convertible_to_db_changed_slot);
6141 if (u->volume_control_volume_changed_slot)
6142 pa_hook_slot_free(u->volume_control_volume_changed_slot);
6144 diff --git a/src/modules/volume-api/mute-control.c b/src/modules/volume-api/mute-control.c
6145 index adc008e..1b2f276 100644
6146 --- a/src/modules/volume-api/mute-control.c
6147 +++ b/src/modules/volume-api/mute-control.c
6150 #include <pulsecore/core-util.h>
6152 -pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description) {
6153 - pa_mute_control *control;
6154 +int pa_mute_control_new(pa_volume_api *api, const char *name, bool persistent, pa_mute_control **_r) {
6155 + pa_mute_control *control = NULL;
6160 - pa_assert(description);
6163 control = pa_xnew0(pa_mute_control, 1);
6164 control->volume_api = api;
6165 control->index = pa_volume_api_allocate_mute_control_index(api);
6166 - pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
6167 - control->description = pa_xstrdup(description);
6169 + r = pa_volume_api_register_name(api, name, false, &control->name);
6173 + control->description = pa_xstrdup(control->name);
6174 control->proplist = pa_proplist_new();
6175 + control->present = !persistent;
6176 + control->persistent = persistent;
6177 + control->purpose = PA_MUTE_CONTROL_PURPOSE_OTHER;
6178 control->devices = pa_hashmap_new(NULL, NULL);
6179 control->default_for_devices = pa_hashmap_new(NULL, NULL);
6180 - control->streams = pa_hashmap_new(NULL, NULL);
6181 - control->audio_groups = pa_hashmap_new(NULL, NULL);
6185 + pa_inidb_row *row;
6187 + row = pa_inidb_table_add_row(api->control_db.mute_controls, control->name);
6188 + control->db_cells.description = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION);
6189 + control->db_cells.mute = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE);
6197 + pa_mute_control_free(control);
6202 -void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
6203 - pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb) {
6204 +void pa_mute_control_put(pa_mute_control *control) {
6205 const char *prop_key;
6209 - pa_assert(initial_mute_is_set || control->set_mute);
6210 - pa_assert(set_initial_mute_cb || !control->set_mute);
6211 + pa_assert(control->set_mute || !control->present);
6213 - if (initial_mute_is_set)
6214 - control->mute = initial_mute;
6216 - control->mute = false;
6217 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED], control);
6218 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE], control);
6220 - if (set_initial_mute_cb)
6221 - set_initial_mute_cb(control);
6222 + if (control->set_mute) {
6223 + control->set_mute_in_progress = true;
6224 + control->set_mute(control, control->mute);
6225 + control->set_mute_in_progress = false;
6228 pa_volume_api_add_mute_control(control->volume_api, control);
6230 control->linked = true;
6232 pa_log_debug("Created mute control #%u.", control->index);
6233 pa_log_debug(" Name: %s", control->name);
6234 pa_log_debug(" Description: %s", control->description);
6235 pa_log_debug(" Mute: %s", pa_yes_no(control->mute));
6236 + pa_log_debug(" Present: %s", pa_yes_no(control->present));
6237 + pa_log_debug(" Persistent: %s", pa_yes_no(control->persistent));
6238 pa_log_debug(" Properties:");
6240 while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
6241 @@ -86,9 +107,7 @@ void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initi
6244 void pa_mute_control_unlink(pa_mute_control *control) {
6245 - pa_audio_group *group;
6247 - pas_stream *stream;
6251 @@ -102,15 +121,9 @@ void pa_mute_control_unlink(pa_mute_control *control) {
6252 pa_log_debug("Unlinking mute control %s.", control->name);
6254 if (control->linked)
6255 - pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
6256 + pa_volume_api_remove_mute_control(control->volume_api, control);
6258 - pa_volume_api_remove_mute_control(control->volume_api, control);
6260 - while ((group = pa_hashmap_first(control->audio_groups)))
6261 - pa_audio_group_set_mute_control(group, NULL);
6263 - while ((stream = pa_hashmap_first(control->streams)))
6264 - pas_stream_set_mute_control(stream, NULL);
6265 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK], control);
6267 while ((device = pa_hashmap_first(control->default_for_devices)))
6268 pa_device_set_default_mute_control(device, NULL);
6269 @@ -133,19 +146,10 @@ void pa_mute_control_unlink(pa_mute_control *control) {
6270 void pa_mute_control_free(pa_mute_control *control) {
6273 - if (!control->unlinked)
6274 + /* unlink() expects name to be set. */
6275 + if (!control->unlinked && control->name)
6276 pa_mute_control_unlink(control);
6278 - if (control->audio_groups) {
6279 - pa_assert(pa_hashmap_isempty(control->audio_groups));
6280 - pa_hashmap_free(control->audio_groups);
6283 - if (control->streams) {
6284 - pa_assert(pa_hashmap_isempty(control->streams));
6285 - pa_hashmap_free(control->streams);
6288 if (control->default_for_devices) {
6289 pa_assert(pa_hashmap_isempty(control->default_for_devices));
6290 pa_hashmap_free(control->default_for_devices);
6291 @@ -167,86 +171,137 @@ void pa_mute_control_free(pa_mute_control *control) {
6295 -void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group) {
6296 +void pa_mute_control_set_purpose(pa_mute_control *control, pa_mute_control_purpose_t purpose, void *owner) {
6299 + pa_assert(!control->linked);
6301 - control->owner_audio_group = group;
6302 + control->purpose = purpose;
6303 + control->owner = owner;
6306 -static void set_mute_internal(pa_mute_control *control, bool mute) {
6309 +int pa_mute_control_acquire_for_audio_group(pa_mute_control *control, pa_audio_group *group,
6310 + pa_mute_control_set_mute_cb_t set_mute_cb, void *userdata) {
6313 + pa_assert(set_mute_cb);
6315 - old_mute = control->mute;
6316 + if (control->present) {
6317 + pa_log("Can't acquire mute control %s, it's already present.", control->name);
6318 + return -PA_ERR_BUSY;
6321 - if (mute == old_mute)
6323 + control->owner_audio_group = group;
6324 + control->set_mute = set_mute_cb;
6325 + control->userdata = userdata;
6327 - control->mute = mute;
6328 + control->set_mute_in_progress = true;
6329 + control->set_mute(control, control->mute);
6330 + control->set_mute_in_progress = false;
6332 + control->present = true;
6334 if (!control->linked || control->unlinked)
6338 - pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_yes_no(old_mute),
6339 - pa_yes_no(control->mute));
6340 + pa_log_debug("Mute control %s became present.", control->name);
6342 - pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
6346 -int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
6349 +void pa_mute_control_release(pa_mute_control *control) {
6352 - if (!control->set_mute) {
6353 - pa_log_info("Tried to set the mute of mute control %s, but the mute control doesn't support the operation.",
6355 - return -PA_ERR_NOTSUPPORTED;
6357 + if (!control->present)
6360 - if (mute == control->mute)
6362 + control->present = false;
6364 - control->set_mute_in_progress = true;
6365 - r = control->set_mute(control, mute);
6366 - control->set_mute_in_progress = false;
6367 + control->userdata = NULL;
6368 + control->set_mute = NULL;
6369 + control->owner_audio_group = NULL;
6372 - set_mute_internal(control, mute);
6373 + if (!control->linked || control->unlinked)
6377 + pa_log_debug("Mute control %s became not present.", control->name);
6380 -void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description) {
6381 +void pa_mute_control_set_description(pa_mute_control *control, const char *description) {
6382 char *old_description;
6385 - pa_assert(new_description);
6386 + pa_assert(description);
6388 old_description = control->description;
6390 - if (pa_streq(new_description, old_description))
6391 + if (pa_streq(description, old_description))
6394 + control->description = pa_xstrdup(description);
6396 + if (control->persistent)
6397 + pa_inidb_cell_set_value(control->db_cells.description, description);
6399 + if (!control->linked || control->unlinked) {
6400 + pa_xfree(old_description);
6404 - control->description = pa_xstrdup(new_description);
6405 pa_log_debug("The description of mute control %s changed from \"%s\" to \"%s\".", control->name, old_description,
6408 pa_xfree(old_description);
6409 pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED], control);
6412 -void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute) {
6413 +static void set_mute_internal(pa_mute_control *control, bool mute) {
6418 - if (!control->linked)
6419 + old_mute = control->mute;
6421 + if (mute == old_mute)
6424 - if (control->set_mute_in_progress)
6425 + control->mute = mute;
6427 + if (control->persistent)
6428 + pa_inidb_cell_set_value(control->db_cells.mute, pa_boolean_to_string(mute));
6430 + if (!control->linked || control->unlinked)
6433 - set_mute_internal(control, new_mute);
6434 + pa_log_debug("The mute of mute control %s changed from %s to %s.", control->name, pa_boolean_to_string(old_mute),
6435 + pa_boolean_to_string(control->mute));
6437 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_MUTE_CONTROL_MUTE_CHANGED], control);
6440 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute) {
6443 + pa_assert(control);
6445 + if (control->set_mute_in_progress)
6448 + if (mute == control->mute)
6451 + if (control->linked && control->present) {
6452 + control->set_mute_in_progress = true;
6453 + r = control->set_mute(control, mute);
6454 + control->set_mute_in_progress = false;
6457 + pa_log("Setting the mute of mute control %s failed.", control->name);
6462 + set_mute_internal(control, mute);
6467 void pa_mute_control_add_device(pa_mute_control *control, pa_device *device) {
6468 @@ -276,31 +331,3 @@ void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_devi
6470 pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
6473 -void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream) {
6474 - pa_assert(control);
6475 - pa_assert(stream);
6477 - pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
6480 -void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream) {
6481 - pa_assert(control);
6482 - pa_assert(stream);
6484 - pa_assert_se(pa_hashmap_remove(control->streams, stream));
6487 -void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group) {
6488 - pa_assert(control);
6491 - pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
6494 -void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group) {
6495 - pa_assert(control);
6498 - pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
6500 diff --git a/src/modules/volume-api/mute-control.h b/src/modules/volume-api/mute-control.h
6501 index 1f70a43..40f8a9c 100644
6502 --- a/src/modules/volume-api/mute-control.h
6503 +++ b/src/modules/volume-api/mute-control.h
6508 +#include <modules/volume-api/inidb.h>
6509 #include <modules/volume-api/volume-api.h>
6511 typedef struct pa_mute_control pa_mute_control;
6514 + PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE,
6515 + PA_MUTE_CONTROL_PURPOSE_OTHER,
6516 +} pa_mute_control_purpose_t;
6518 +typedef int (*pa_mute_control_set_mute_cb_t)(pa_mute_control *control, bool mute);
6520 struct pa_mute_control {
6521 pa_volume_api *volume_api;
6523 @@ -33,6 +41,14 @@ struct pa_mute_control {
6525 pa_proplist *proplist;
6530 + pa_mute_control_purpose_t purpose;
6532 + pas_stream *owner_stream;
6536 /* If this mute control is the "own mute control" of an audio group, this
6537 * is set to point to that group, otherwise this is NULL. */
6538 @@ -40,50 +56,43 @@ struct pa_mute_control {
6540 pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
6541 pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
6542 - pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
6543 - pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
6546 + pa_inidb_cell *description;
6547 + pa_inidb_cell *mute;
6552 bool set_mute_in_progress;
6554 /* Called from pa_mute_control_set_mute(). The implementation is expected
6555 - * to return a negative error code on failure. May be NULL, if the mute
6556 - * control is read-only. */
6557 - int (*set_mute)(pa_mute_control *control, bool mute);
6558 + * to return a negative error code on failure. */
6559 + pa_mute_control_set_mute_cb_t set_mute;
6564 -pa_mute_control *pa_mute_control_new(pa_volume_api *api, const char *name, const char *description);
6566 -typedef void (*pa_mute_control_set_initial_mute_cb_t)(pa_mute_control *control);
6568 -/* initial_mute is the preferred initial mute of the mute control
6569 - * implementation. It may be unset, if the implementation doesn't care about
6570 - * the initial state of the mute control. Read-only mute controls, however,
6571 - * must always set initial_mute.
6573 - * The implementation's initial mute preference may be overridden by policy, if
6574 - * the mute control isn't read-only. When the final initial mute is known, the
6575 - * the implementation is notified via set_initial_mute_cb (the mute can be read
6576 - * from control->mute). set_initial_mute_cb may be NULL, if the mute control is
6578 -void pa_mute_control_put(pa_mute_control *control, bool initial_mute, bool initial_mute_is_set,
6579 - pa_mute_control_set_initial_mute_cb_t set_initial_mute_cb);
6581 +int pa_mute_control_new(pa_volume_api *api, const char *name, bool persistent, pa_mute_control **_r);
6582 +void pa_mute_control_put(pa_mute_control *control);
6583 void pa_mute_control_unlink(pa_mute_control *control);
6584 void pa_mute_control_free(pa_mute_control *control);
6586 -/* Called by audio-group.c only. */
6587 -void pa_mute_control_set_owner_audio_group(pa_mute_control *control, pa_audio_group *group);
6589 -/* Called by clients and policy modules. */
6590 -int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
6591 +/* Called by the mute control implementation, before pa_mute_control_put(). */
6592 +void pa_mute_control_set_purpose(pa_mute_control *control, pa_mute_control_purpose_t purpose, void *owner);
6594 /* Called by the mute control implementation. */
6595 -void pa_mute_control_description_changed(pa_mute_control *control, const char *new_description);
6596 -void pa_mute_control_mute_changed(pa_mute_control *control, bool new_mute);
6597 +int pa_mute_control_acquire_for_audio_group(pa_mute_control *control, pa_audio_group *group,
6598 + pa_mute_control_set_mute_cb_t set_mute_cb, void *userdata);
6600 +/* Called by the mute control implementation. This must only be called for
6601 + * persistent controls; use pa_mute_control_free() for non-persistent
6603 +void pa_mute_control_release(pa_mute_control *control);
6605 +/* Called by anyone. */
6606 +void pa_mute_control_set_description(pa_mute_control *control, const char *description);
6607 +int pa_mute_control_set_mute(pa_mute_control *control, bool mute);
6609 /* Called from device.c only. */
6610 void pa_mute_control_add_device(pa_mute_control *control, pa_device *device);
6611 @@ -91,12 +100,4 @@ void pa_mute_control_remove_device(pa_mute_control *control, pa_device *device);
6612 void pa_mute_control_add_default_for_device(pa_mute_control *control, pa_device *device);
6613 void pa_mute_control_remove_default_for_device(pa_mute_control *control, pa_device *device);
6615 -/* Called from sstream.c only. */
6616 -void pa_mute_control_add_stream(pa_mute_control *control, pas_stream *stream);
6617 -void pa_mute_control_remove_stream(pa_mute_control *control, pas_stream *stream);
6619 -/* Called from audio-group.c only. */
6620 -void pa_mute_control_add_audio_group(pa_mute_control *control, pa_audio_group *group);
6621 -void pa_mute_control_remove_audio_group(pa_mute_control *control, pa_audio_group *group);
6624 diff --git a/src/modules/volume-api/sstream.c b/src/modules/volume-api/sstream.c
6625 index e3531a8..1738d15 100644
6626 --- a/src/modules/volume-api/sstream.c
6627 +++ b/src/modules/volume-api/sstream.c
6629 #include "sstream.h"
6631 #include <modules/volume-api/audio-group.h>
6632 -#include <modules/volume-api/binding.h>
6633 #include <modules/volume-api/mute-control.h>
6634 #include <modules/volume-api/volume-control.h>
6636 @@ -34,121 +33,43 @@
6638 #include <pulsecore/core-util.h>
6640 -pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction) {
6641 - pas_stream *stream;
6642 +int pas_stream_new(pa_volume_api *api, const char *name, pas_stream **_r) {
6643 + pas_stream *stream = NULL;
6648 - pa_assert(description);
6651 stream = pa_xnew0(pas_stream, 1);
6652 stream->volume_api = api;
6653 stream->index = pa_volume_api_allocate_stream_index(api);
6654 - pa_assert_se(pa_volume_api_register_name(api, name, false, &stream->name) >= 0);
6655 - stream->description = pa_xstrdup(description);
6656 - stream->direction = direction;
6657 - stream->proplist = pa_proplist_new();
6658 - stream->use_default_volume_control = true;
6659 - stream->use_default_mute_control = true;
6664 -static void set_volume_control_internal(pas_stream *stream, pa_volume_control *control) {
6665 - pa_volume_control *old_control;
6667 - pa_assert(stream);
6669 - old_control = stream->volume_control;
6671 - if (control == old_control)
6674 - if (old_control) {
6675 - /* If the old control pointed to the own volume control of an audio
6676 - * group, then the stream's audio group for volume needs to be
6677 - * updated. We set it to NULL here, and if it should be non-NULL, that
6678 - * will be fixed very soon (a few lines down). */
6679 - pas_stream_set_audio_group_for_volume(stream, NULL);
6681 - pa_volume_control_remove_stream(old_control, stream);
6684 - stream->volume_control = control;
6687 - pa_volume_control_add_stream(control, stream);
6688 - pas_stream_set_audio_group_for_volume(stream, control->owner_audio_group);
6691 - if (!stream->linked || stream->unlinked)
6694 - pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
6695 - old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6697 - pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
6700 -static void set_mute_control_internal(pas_stream *stream, pa_mute_control *control) {
6701 - pa_mute_control *old_control;
6703 - pa_assert(stream);
6705 - old_control = stream->mute_control;
6707 - if (control == old_control)
6709 + r = pa_volume_api_register_name(api, name, false, &stream->name);
6713 - if (old_control) {
6714 - /* If the old control pointed to the own mute control of an audio
6715 - * group, then the stream's audio group for mute needs to be updated.
6716 - * We set it to NULL here, and if it should be non-NULL, that will be
6717 - * fixed very soon (a few lines down). */
6718 - pas_stream_set_audio_group_for_mute(stream, NULL);
6720 - pa_mute_control_remove_stream(old_control, stream);
6723 - stream->mute_control = control;
6726 - pa_mute_control_add_stream(control, stream);
6727 - pas_stream_set_audio_group_for_mute(stream, control->owner_audio_group);
6729 + stream->description = pa_xstrdup(stream->name);
6730 + stream->direction = PA_DIRECTION_OUTPUT;
6731 + stream->proplist = pa_proplist_new();
6733 - if (!stream->linked || stream->unlinked)
6738 - pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
6739 - old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6742 + pas_stream_free(stream);
6744 - pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
6748 -void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
6749 +void pas_stream_put(pas_stream *stream) {
6750 const char *prop_key;
6754 - pa_assert(!stream->create_own_volume_control || stream->delete_own_volume_control);
6755 - pa_assert(!stream->create_own_mute_control || stream->delete_own_mute_control);
6757 - if (initial_properties)
6758 - pa_proplist_update(stream->proplist, PA_UPDATE_REPLACE, initial_properties);
6760 - pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL], stream);
6762 - if (stream->use_default_volume_control)
6763 - set_volume_control_internal(stream, stream->own_volume_control);
6765 - pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL], stream);
6767 - if (stream->use_default_mute_control)
6768 - set_mute_control_internal(stream, stream->own_mute_control);
6770 pa_volume_api_add_stream(stream->volume_api, stream);
6772 stream->linked = true;
6774 pa_log_debug("Created stream #%u.", stream->index);
6775 @@ -157,6 +78,10 @@ void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties) {
6776 pa_log_debug(" Direction: %s", pa_direction_to_string(stream->direction));
6777 pa_log_debug(" Volume control: %s", stream->volume_control ? stream->volume_control->name : "(unset)");
6778 pa_log_debug(" Mute control: %s", stream->mute_control ? stream->mute_control->name : "(unset)");
6779 + pa_log_debug(" Audio group for volume: %s",
6780 + stream->audio_group_for_volume ? stream->audio_group_for_volume->name : "(unset)");
6781 + pa_log_debug(" Audio group for mute: %s",
6782 + stream->audio_group_for_mute ? stream->audio_group_for_mute->name : "(unset)");
6783 pa_log_debug(" Properties:");
6785 while ((prop_key = pa_proplist_iterate(stream->proplist, &state)))
6786 @@ -178,22 +103,22 @@ void pas_stream_unlink(pas_stream *stream) {
6787 pa_log_debug("Unlinking stream %s.", stream->name);
6790 - pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
6791 + pa_volume_api_remove_stream(stream->volume_api, stream);
6793 - pa_volume_api_remove_stream(stream->volume_api, stream);
6794 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_UNLINK], stream);
6796 pas_stream_set_audio_group_for_mute(stream, NULL);
6797 pas_stream_set_audio_group_for_volume(stream, NULL);
6798 pas_stream_set_mute_control(stream, NULL);
6799 + pas_stream_set_relative_volume_control(stream, NULL);
6800 pas_stream_set_volume_control(stream, NULL);
6801 - pas_stream_set_have_own_mute_control(stream, false);
6802 - pas_stream_set_have_own_volume_control(stream, false);
6805 void pas_stream_free(pas_stream *stream) {
6808 - if (!stream->unlinked)
6809 + /* unlink() expects name to be set. */
6810 + if (!stream->unlinked && stream->name)
6811 pas_stream_unlink(stream);
6813 if (stream->proplist)
6814 @@ -207,160 +132,172 @@ void pas_stream_free(pas_stream *stream) {
6818 -int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have) {
6819 +void pas_stream_set_direction(pas_stream *stream, pa_direction_t direction) {
6821 + pa_assert(!stream->linked);
6823 - if (have == stream->have_own_volume_control)
6825 + stream->direction = direction;
6829 - pa_assert(!stream->own_volume_control);
6830 +void pas_stream_set_description(pas_stream *stream, const char *description) {
6831 + char *old_description;
6833 + pa_assert(stream);
6834 + pa_assert(description);
6836 - if (!stream->create_own_volume_control) {
6837 - pa_log_debug("Stream %s doesn't support own volume control.", stream->name);
6838 - return -PA_ERR_NOTSUPPORTED;
6840 + old_description = stream->description;
6842 + if (pa_streq(description, old_description))
6845 - stream->own_volume_control = stream->create_own_volume_control(stream);
6847 - stream->delete_own_volume_control(stream);
6848 - stream->own_volume_control = NULL;
6849 + stream->description = pa_xstrdup(description);
6851 + if (!stream->linked || stream->unlinked) {
6852 + pa_xfree(old_description);
6856 - stream->have_own_volume_control = have;
6857 + pa_log_debug("Stream %s description changed from \"%s\" to \"%s\".", stream->name, old_description,
6859 + pa_xfree(old_description);
6862 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
6865 -int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have) {
6866 +void pas_stream_set_property(pas_stream *stream, const char *key, const char *value) {
6867 + const char *old_value;
6872 - if (have == stream->have_own_mute_control)
6874 + old_value = pa_proplist_gets(stream->proplist, key);
6877 - pa_assert(!stream->own_mute_control);
6878 + if (pa_safe_streq(value, old_value))
6881 - if (!stream->create_own_mute_control) {
6882 - pa_log_debug("Stream %s doesn't support own mute control.", stream->name);
6883 - return -PA_ERR_NOTSUPPORTED;
6886 + pa_proplist_sets(stream->proplist, key, value);
6888 + pa_proplist_unset(stream->proplist, key);
6890 - stream->own_mute_control = stream->create_own_mute_control(stream);
6892 - stream->delete_own_mute_control(stream);
6893 - stream->own_mute_control = NULL;
6895 + if (!stream->linked || stream->unlinked)
6898 - stream->have_own_mute_control = have;
6899 + pa_log_debug("Stream %s property \"%s\" changed from \"%s\" to \"%s\".", stream->name, key,
6900 + old_value ? old_value : "(unset)", value ? value : "(unset)");
6903 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED], stream);
6906 void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control) {
6907 - pa_assert(stream);
6908 + pa_volume_control *old_control;
6910 - stream->use_default_volume_control = false;
6911 + pa_assert(stream);
6913 - if (stream->volume_control_binding) {
6914 - pa_binding_free(stream->volume_control_binding);
6915 - stream->volume_control_binding = NULL;
6917 + old_control = stream->volume_control;
6919 - set_volume_control_internal(stream, control);
6921 + if (control == old_control)
6924 -void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
6925 - pa_assert(stream);
6926 + stream->volume_control = control;
6928 - stream->use_default_mute_control = false;
6929 + if (!stream->linked || stream->unlinked)
6932 - if (stream->mute_control_binding) {
6933 - pa_binding_free(stream->mute_control_binding);
6934 - stream->mute_control_binding = NULL;
6936 + pa_log_debug("The volume control of stream %s changed from %s to %s.", stream->name,
6937 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6939 - set_mute_control_internal(stream, control);
6940 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED], stream);
6943 -void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info) {
6944 - pa_binding_owner_info owner_info = {
6945 - .userdata = stream,
6946 - .set_value = (pa_binding_set_value_cb_t) set_volume_control_internal,
6948 +void pas_stream_set_relative_volume_control(pas_stream *stream, pa_volume_control *control) {
6949 + pa_volume_control *old_control;
6952 - pa_assert(target_info);
6954 - stream->use_default_volume_control = false;
6956 - if (stream->volume_control_binding)
6957 - pa_binding_free(stream->volume_control_binding);
6958 + old_control = stream->relative_volume_control;
6960 - stream->volume_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
6962 + if (control == old_control)
6965 -void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info) {
6966 - pa_binding_owner_info owner_info = {
6967 - .userdata = stream,
6968 - .set_value = (pa_binding_set_value_cb_t) set_mute_control_internal,
6970 + stream->relative_volume_control = control;
6972 - pa_assert(stream);
6973 - pa_assert(target_info);
6975 - stream->use_default_mute_control = false;
6976 + if (!stream->linked || stream->unlinked)
6979 - if (stream->mute_control_binding)
6980 - pa_binding_free(stream->mute_control_binding);
6981 + pa_log_debug("The relative volume control of stream %s changed from %s to %s.", stream->name,
6982 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
6984 - stream->mute_control_binding = pa_binding_new(stream->volume_api, &owner_info, target_info);
6985 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED], stream);
6988 -void pas_stream_description_changed(pas_stream *stream, const char *new_description) {
6989 - char *old_description;
6990 +void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control) {
6991 + pa_mute_control *old_control;
6994 - pa_assert(new_description);
6996 - old_description = stream->description;
6997 + old_control = stream->mute_control;
6999 - if (pa_streq(new_description, old_description))
7000 + if (control == old_control)
7003 - stream->description = pa_xstrdup(new_description);
7004 - pa_log_debug("The description of stream %s changed from \"%s\" to \"%s\".", stream->name, old_description,
7006 - pa_xfree(old_description);
7007 - pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED], stream);
7008 + stream->mute_control = control;
7010 + if (!stream->linked || stream->unlinked)
7013 + pa_log_debug("The mute control of stream %s changed from %s to %s.", stream->name,
7014 + old_control ? old_control->name : "(unset)", control ? control->name : "(unset)");
7016 + pa_hook_fire(&stream->volume_api->hooks[PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED], stream);
7019 void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group) {
7020 + pa_audio_group *old_group;
7024 - if (group == stream->audio_group_for_volume)
7025 + old_group = stream->audio_group_for_volume;
7027 + if (group == old_group)
7030 - if (stream->audio_group_for_volume)
7031 - pa_audio_group_remove_volume_stream(stream->audio_group_for_volume, stream);
7033 + pa_audio_group_remove_volume_stream(old_group, stream);
7035 stream->audio_group_for_volume = group;
7038 pa_audio_group_add_volume_stream(group, stream);
7040 + if (!stream->linked || stream->unlinked)
7043 + pa_log_debug("Stream %s audio group for volume changed from %s to %s.", stream->name,
7044 + old_group ? old_group->name : "(unset)", group ? group->name : "(unset)");
7047 void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group) {
7048 + pa_audio_group *old_group;
7052 - if (group == stream->audio_group_for_mute)
7053 + old_group = stream->audio_group_for_mute;
7055 + if (group == old_group)
7058 - if (stream->audio_group_for_mute)
7059 - pa_audio_group_remove_mute_stream(stream->audio_group_for_mute, stream);
7061 + pa_audio_group_remove_mute_stream(old_group, stream);
7063 stream->audio_group_for_mute = group;
7066 pa_audio_group_add_mute_stream(group, stream);
7068 + if (!stream->linked || stream->unlinked)
7071 + pa_log_debug("Stream %s audio group for mute changed from %s to %s.", stream->name,
7072 + old_group ? old_group->name : "(unset)", group ? group->name : "(unset)");
7074 diff --git a/src/modules/volume-api/sstream.h b/src/modules/volume-api/sstream.h
7075 index a65b34c..715bf2c 100644
7076 --- a/src/modules/volume-api/sstream.h
7077 +++ b/src/modules/volume-api/sstream.h
7078 @@ -39,69 +39,33 @@ struct pas_stream {
7079 pa_direction_t direction;
7080 pa_proplist *proplist;
7081 pa_volume_control *volume_control;
7082 + pa_volume_control *relative_volume_control;
7083 pa_mute_control *mute_control;
7084 - bool use_default_volume_control;
7085 - bool use_default_mute_control;
7086 - bool have_own_volume_control;
7087 - bool have_own_mute_control;
7088 - pa_volume_control *own_volume_control;
7089 - pa_mute_control *own_mute_control;
7091 - pa_binding *volume_control_binding;
7092 - pa_binding *mute_control_binding;
7093 pa_audio_group *audio_group_for_volume;
7094 pa_audio_group *audio_group_for_mute;
7099 - /* Called when the own volume control is enabled. The callback
7100 - * implementation should return a new linked volume control object. The
7101 - * callback may be NULL, in which case the own volume control can't be
7103 - pa_volume_control *(*create_own_volume_control)(pas_stream *stream);
7105 - /* Called when the own volume control is disabled. The implementation
7106 - * should free stream->own_volume_control. The callback may be NULL only if
7107 - * create_own_volume_control is NULL also. */
7108 - void (*delete_own_volume_control)(pas_stream *stream);
7110 - /* Called when the own mute control is enabled. The callback implementation
7111 - * should return a new linked mute control object. The callback may be
7112 - * NULL, in which case the own mute control can't be enabled. */
7113 - pa_mute_control *(*create_own_mute_control)(pas_stream *stream);
7115 - /* Called when the own mute control is disabled. The implementation should
7116 - * free stream->own_mute_control. The callback may be NULL only if
7117 - * create_own_mute_control is NULL also. */
7118 - void (*delete_own_mute_control)(pas_stream *stream);
7123 -pas_stream *pas_stream_new(pa_volume_api *api, const char *name, const char *description, pa_direction_t direction);
7124 -void pas_stream_put(pas_stream *stream, pa_proplist *initial_properties);
7125 +int pas_stream_new(pa_volume_api *api, const char *name, pas_stream **_r);
7126 +void pas_stream_put(pas_stream *stream);
7127 void pas_stream_unlink(pas_stream *stream);
7128 void pas_stream_free(pas_stream *stream);
7130 -/* Called by the stream implementation and possibly by policy modules.
7131 - * Enabling own controls may fail (the stream may not support own controls),
7132 - * disabling will never fail. */
7133 -int pas_stream_set_have_own_volume_control(pas_stream *stream, bool have);
7134 -int pas_stream_set_have_own_mute_control(pas_stream *stream, bool have);
7135 +/* Called by the stream implementation, only during initialization. */
7136 +void pas_stream_set_direction(pas_stream *stream, pa_direction_t direction);
7138 -/* Called by policy modules. */
7139 +/* Called by the stream implementation. */
7140 +void pas_stream_set_description(pas_stream *stream, const char *description);
7141 +void pas_stream_set_property(pas_stream *stream, const char *key, const char *value);
7142 void pas_stream_set_volume_control(pas_stream *stream, pa_volume_control *control);
7143 +void pas_stream_set_relative_volume_control(pas_stream *stream, pa_volume_control *control);
7144 void pas_stream_set_mute_control(pas_stream *stream, pa_mute_control *control);
7145 -void pas_stream_bind_volume_control(pas_stream *stream, pa_binding_target_info *target_info);
7146 -void pas_stream_bind_mute_control(pas_stream *stream, pa_binding_target_info *target_info);
7148 -/* Called by the stream implementation. */
7149 -void pas_stream_description_changed(pas_stream *stream, const char *new_description);
7151 -/* Called by audio-group.c only. Adding a stream to an audio group happens
7152 - * implicitly when the volume or mute control of a stream is set to point to
7153 - * the own control of an audio group. */
7154 +/* Called by anyone. */
7155 void pas_stream_set_audio_group_for_volume(pas_stream *stream, pa_audio_group *group);
7156 void pas_stream_set_audio_group_for_mute(pas_stream *stream, pa_audio_group *group);
7158 diff --git a/src/modules/volume-api/stream-creator.c b/src/modules/volume-api/stream-creator.c
7159 index f6ca7b3..0d9ea24 100644
7160 --- a/src/modules/volume-api/stream-creator.c
7161 +++ b/src/modules/volume-api/stream-creator.c
7163 struct pa_stream_creator {
7164 pa_volume_api *volume_api;
7165 pa_hashmap *streams; /* pa_sink_input/pa_source_output -> struct stream */
7166 - pa_hook_slot *sink_input_put_slot;
7167 + pa_hook_slot *sink_input_fixate_slot;
7168 pa_hook_slot *sink_input_unlink_slot;
7169 - pa_hook_slot *source_output_put_slot;
7170 + pa_hook_slot *source_output_fixate_slot;
7171 pa_hook_slot *source_output_unlink_slot;
7174 @@ -47,73 +47,61 @@ enum stream_type {
7179 pa_stream_creator *creator;
7180 enum stream_type type;
7181 + pa_sink_input_new_data *sink_input_new_data;
7182 pa_sink_input *sink_input;
7183 + pa_source_output_new_data *source_output_new_data;
7184 pa_source_output *source_output;
7186 + pa_volume_control *volume_control;
7187 + pa_volume_control *relative_volume_control;
7188 + pa_mute_control *mute_control;
7193 pa_hook_slot *proplist_changed_slot;
7194 - pa_hook_slot *client_proplist_changed_slot;
7195 pa_hook_slot *volume_changed_slot;
7196 + pa_hook_slot *reference_ratio_changed_slot;
7197 pa_hook_slot *mute_changed_slot;
7200 -static char *get_stream_volume_and_mute_control_description_malloc(struct stream *stream) {
7201 - const char *application_name = NULL;
7202 - char *description;
7204 - pa_assert(stream);
7206 - if (stream->client)
7207 - application_name = pa_proplist_gets(stream->client->proplist, PA_PROP_APPLICATION_NAME);
7209 - if (application_name)
7210 - description = pa_sprintf_malloc("%s: %s", application_name, stream->stream->description);
7212 - description = pa_xstrdup(stream->stream->description);
7214 - return description;
7216 +static void stream_free(struct stream *stream);
7218 -static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
7219 +static int volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *original_volume,
7220 + const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
7221 struct stream *stream;
7226 - pa_assert(volume);
7227 + pa_assert(original_volume);
7228 + pa_assert(remapped_volume);
7230 stream = control->userdata;
7232 - switch (stream->type) {
7233 - case STREAM_TYPE_SINK_INPUT:
7234 - pa_bvolume_from_cvolume(&bvolume, &stream->sink_input->volume, &stream->sink_input->channel_map);
7237 - case STREAM_TYPE_SOURCE_OUTPUT:
7238 - pa_bvolume_from_cvolume(&bvolume, &stream->source_output->volume, &stream->source_output->channel_map);
7241 + bvolume = control->volume;
7244 - bvolume.volume = volume->volume;
7245 + bvolume.volume = remapped_volume->volume;
7248 - pa_bvolume_copy_balance(&bvolume, volume);
7249 + pa_bvolume_copy_balance(&bvolume, remapped_volume);
7251 pa_bvolume_to_cvolume(&bvolume, &cvolume);
7253 switch (stream->type) {
7254 case STREAM_TYPE_SINK_INPUT:
7255 - pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
7256 + if (stream->sink_input->state == PA_SINK_INPUT_INIT)
7257 + pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, false);
7259 + pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
7262 case STREAM_TYPE_SOURCE_OUTPUT:
7263 - pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
7264 + if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT)
7265 + pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, false);
7267 + pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
7271 @@ -129,6 +117,9 @@ static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook
7273 pa_assert(call_data);
7275 + if (!stream->volume_control)
7276 + return PA_HOOK_OK;
7278 switch (stream->type) {
7279 case STREAM_TYPE_SINK_INPUT:
7281 @@ -144,63 +135,95 @@ static pa_hook_result_t sink_input_or_source_output_volume_changed_cb(void *hook
7284 pa_bvolume_from_cvolume(&bvolume, &input->volume, &input->channel_map);
7287 pa_bvolume_from_cvolume(&bvolume, &output->volume, &output->channel_map);
7289 + pa_assert_not_reached();
7291 - pa_volume_control_volume_changed(stream->stream->own_volume_control, &bvolume, true, true);
7292 + pa_volume_control_set_volume(stream->volume_control, &bvolume, true, true);
7297 -static void volume_control_set_initial_volume_cb(pa_volume_control *control) {
7298 +static int relative_volume_control_set_volume_cb(pa_volume_control *control, const pa_bvolume *original_volume,
7299 + const pa_bvolume *remapped_volume, bool set_volume, bool set_balance) {
7300 struct stream *stream;
7301 + pa_bvolume bvolume;
7305 + pa_assert(original_volume);
7306 + pa_assert(remapped_volume);
7308 stream = control->userdata;
7309 - pa_bvolume_to_cvolume(&control->volume, &cvolume);
7311 - switch (stream->type) {
7312 - case STREAM_TYPE_SINK_INPUT:
7313 - pa_sink_input_set_volume(stream->sink_input, &cvolume, true, true);
7315 + bvolume = control->volume;
7317 - case STREAM_TYPE_SOURCE_OUTPUT:
7318 - pa_source_output_set_volume(stream->source_output, &cvolume, true, true);
7323 -static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
7324 - struct stream *stream;
7326 + bvolume.volume = remapped_volume->volume;
7328 - pa_assert(control);
7330 + pa_bvolume_copy_balance(&bvolume, remapped_volume);
7332 - stream = control->userdata;
7333 + pa_bvolume_to_cvolume(&bvolume, &cvolume);
7335 switch (stream->type) {
7336 case STREAM_TYPE_SINK_INPUT:
7337 - pa_sink_input_set_mute(stream->sink_input, mute, true);
7338 + if (stream->sink_input->state == PA_SINK_INPUT_INIT) {
7339 + pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, true);
7341 + /* XXX: This is a bit ugly. This is needed, because when we
7342 + * call pa_sink_input_new_data_set_volume(), there's no
7343 + * automatic notification to the primary volume control object
7344 + * about the changed volume. This problem should go away once
7345 + * stream volume controls are moved into the core. */
7346 + if (stream->volume_control) {
7347 + pa_bvolume absolute_volume;
7349 + pa_bvolume_from_cvolume(&absolute_volume, &stream->sink_input_new_data->volume,
7350 + &stream->sink_input_new_data->channel_map);
7351 + pa_volume_control_set_volume(stream->volume_control, &absolute_volume, true, true);
7354 + pa_sink_input_set_volume(stream->sink_input, &cvolume, true, false);
7357 case STREAM_TYPE_SOURCE_OUTPUT:
7358 - pa_source_output_set_mute(stream->source_output, mute, true);
7359 + if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT) {
7360 + pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, true);
7362 + /* XXX: This is a bit ugly. This is needed, because when we
7363 + * call pa_source_output_new_data_set_volume(), there's no
7364 + * automatic notification to the primary volume control object
7365 + * about the changed volume. This problem should go away once
7366 + * stream volume controls are moved into the core. */
7367 + if (stream->volume_control) {
7368 + pa_bvolume absolute_volume;
7370 + pa_bvolume_from_cvolume(&absolute_volume, &stream->source_output_new_data->volume,
7371 + &stream->source_output_new_data->channel_map);
7372 + pa_volume_control_set_volume(stream->volume_control, &absolute_volume, true, true);
7375 + pa_source_output_set_volume(stream->source_output, &cvolume, true, false);
7382 -static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
7383 +static pa_hook_result_t sink_input_or_source_output_reference_ratio_changed_cb(void *hook_data, void *call_data,
7385 struct stream *stream = userdata;
7386 pa_sink_input *input = NULL;
7387 pa_source_output *output = NULL;
7389 + pa_bvolume bvolume;
7392 pa_assert(call_data);
7394 + if (!stream->relative_volume_control)
7395 + return PA_HOOK_OK;
7397 switch (stream->type) {
7398 case STREAM_TYPE_SINK_INPUT:
7400 @@ -215,18 +238,18 @@ static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_d
7404 - mute = input->muted;
7405 + pa_bvolume_from_cvolume(&bvolume, &input->reference_ratio, &input->channel_map);
7407 - mute = output->muted;
7408 + pa_bvolume_from_cvolume(&bvolume, &output->reference_ratio, &output->channel_map);
7410 pa_assert_not_reached();
7412 - pa_mute_control_mute_changed(stream->stream->own_mute_control, mute);
7413 + pa_volume_control_set_volume(stream->relative_volume_control, &bvolume, true, true);
7418 -static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
7419 +static int mute_control_set_mute_cb(pa_mute_control *control, bool mute) {
7420 struct stream *stream;
7423 @@ -235,167 +258,66 @@ static void mute_control_set_initial_mute_cb(pa_mute_control *control) {
7425 switch (stream->type) {
7426 case STREAM_TYPE_SINK_INPUT:
7427 - pa_sink_input_set_mute(stream->sink_input, control->mute, true);
7430 - case STREAM_TYPE_SOURCE_OUTPUT:
7431 - pa_source_output_set_mute(stream->source_output, control->mute, true);
7436 -static const char *get_sink_input_description(pa_sink_input *input) {
7437 - const char *description;
7441 - description = pa_proplist_gets(input->proplist, PA_PROP_MEDIA_NAME);
7443 - return description;
7448 -static const char *get_source_output_description(pa_source_output *output) {
7449 - const char *description;
7451 - pa_assert(output);
7453 - description = pa_proplist_gets(output->proplist, PA_PROP_MEDIA_NAME);
7455 - return description;
7460 -static pa_volume_control *stream_create_own_volume_control_cb(pas_stream *s) {
7461 - struct stream *stream;
7462 - const char *name = NULL;
7463 - char *description;
7464 - pa_volume_control *control;
7465 - pa_bvolume volume;
7469 - stream = s->userdata;
7471 - switch (stream->type) {
7472 - case STREAM_TYPE_SINK_INPUT:
7473 - name = "sink-input-volume-control";
7476 - case STREAM_TYPE_SOURCE_OUTPUT:
7477 - name = "source-output-volume-control";
7481 - description = get_stream_volume_and_mute_control_description_malloc(stream);
7482 - control = pa_volume_control_new(stream->creator->volume_api, name, description, true, false);
7483 - pa_xfree(description);
7484 - control->set_volume = volume_control_set_volume_cb;
7485 - control->userdata = stream;
7487 - pa_assert(!stream->volume_changed_slot);
7489 - switch (stream->type) {
7490 - case STREAM_TYPE_SINK_INPUT:
7491 - stream->volume_changed_slot =
7492 - pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
7493 - sink_input_or_source_output_volume_changed_cb, stream);
7494 - pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, &stream->sink_input->channel_map);
7495 + if (stream->sink_input->state == PA_SINK_INPUT_INIT)
7496 + pa_sink_input_new_data_set_muted(stream->sink_input_new_data, mute);
7498 + pa_sink_input_set_mute(stream->sink_input, mute, true);
7501 case STREAM_TYPE_SOURCE_OUTPUT:
7502 - stream->volume_changed_slot =
7503 - pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED],
7504 - PA_HOOK_NORMAL, sink_input_or_source_output_volume_changed_cb, stream);
7505 - pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, &stream->source_output->channel_map);
7506 + if (stream->source_output->state == PA_SOURCE_OUTPUT_INIT)
7507 + pa_source_output_new_data_set_muted(stream->source_output_new_data, mute);
7509 + pa_source_output_set_mute(stream->source_output, mute, true);
7513 - pa_volume_control_put(control, &volume, volume_control_set_initial_volume_cb);
7518 -static void stream_delete_own_volume_control_cb(pas_stream *s) {
7519 - struct stream *stream;
7523 - stream = s->userdata;
7524 - pa_hook_slot_free(stream->volume_changed_slot);
7525 - stream->volume_changed_slot = NULL;
7526 - pa_volume_control_free(s->own_volume_control);
7530 -static pa_mute_control *stream_create_own_mute_control_cb(pas_stream *s) {
7531 - struct stream *stream;
7532 - const char *name = NULL;
7533 - char *description;
7534 - pa_mute_control *control;
7535 - bool mute = false;
7539 - stream = s->userdata;
7541 - switch (stream->type) {
7542 - case STREAM_TYPE_SINK_INPUT:
7543 - name = "sink-input-mute-control";
7546 - case STREAM_TYPE_SOURCE_OUTPUT:
7547 - name = "source-output-mute-control";
7550 +static pa_hook_result_t sink_input_or_source_output_mute_changed_cb(void *hook_data, void *call_data, void *userdata) {
7551 + struct stream *stream = userdata;
7552 + pa_sink_input *input = NULL;
7553 + pa_source_output *output = NULL;
7556 - description = get_stream_volume_and_mute_control_description_malloc(stream);
7557 - control = pa_mute_control_new(stream->creator->volume_api, name, description);
7558 - pa_xfree(description);
7559 - control->set_mute = mute_control_set_mute_cb;
7560 - control->userdata = stream;
7561 + pa_assert(stream);
7562 + pa_assert(call_data);
7564 - pa_assert(!stream->mute_changed_slot);
7565 + if (!stream->mute_control)
7566 + return PA_HOOK_OK;
7568 switch (stream->type) {
7569 case STREAM_TYPE_SINK_INPUT:
7570 - stream->mute_changed_slot =
7571 - pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
7572 - sink_input_or_source_output_mute_changed_cb, stream);
7573 - mute = stream->sink_input->muted;
7574 + input = call_data;
7577 case STREAM_TYPE_SOURCE_OUTPUT:
7578 - stream->mute_changed_slot =
7579 - pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED],
7580 - PA_HOOK_NORMAL, sink_input_or_source_output_mute_changed_cb, stream);
7581 - mute = stream->source_output->muted;
7582 + output = call_data;
7586 - pa_mute_control_put(control, mute, true, mute_control_set_initial_mute_cb);
7590 + if ((input && input != stream->sink_input) || (output && output != stream->source_output))
7591 + return PA_HOOK_OK;
7593 -static void stream_delete_own_mute_control_cb(pas_stream *s) {
7594 - struct stream *stream;
7596 + mute = input->muted;
7598 + mute = output->muted;
7600 + pa_assert_not_reached();
7603 + pa_mute_control_set_mute(stream->mute_control, mute);
7605 - stream = s->userdata;
7606 - pa_hook_slot_free(stream->mute_changed_slot);
7607 - stream->mute_changed_slot = NULL;
7608 - pa_mute_control_free(s->own_mute_control);
7609 + return PA_HOOK_OK;
7612 static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
7613 struct stream *stream = userdata;
7614 pa_sink_input *input = NULL;
7615 pa_source_output *output = NULL;
7616 - const char *new_stream_description = NULL;
7617 - char *new_control_description;
7618 + pa_proplist *proplist = NULL;
7619 + const char *description = NULL;
7622 pa_assert(call_data);
7623 @@ -407,9 +329,7 @@ static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *ho
7624 if (input != stream->sink_input)
7627 - new_stream_description = get_sink_input_description(input);
7628 - if (!new_stream_description)
7629 - new_stream_description = stream->stream->name;
7630 + proplist = stream->sink_input->proplist;
7633 case STREAM_TYPE_SOURCE_OUTPUT:
7634 @@ -418,187 +338,290 @@ static pa_hook_result_t sink_input_or_source_output_proplist_changed_cb(void *ho
7635 if (output != stream->source_output)
7638 - new_stream_description = get_source_output_description(output);
7639 - if (!new_stream_description)
7640 - new_stream_description = stream->stream->name;
7641 + proplist = stream->source_output->proplist;
7645 - pas_stream_description_changed(stream->stream, new_stream_description);
7647 - new_control_description = get_stream_volume_and_mute_control_description_malloc(stream);
7648 + description = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
7650 + description = stream->stream->name;
7652 - if (stream->stream->own_volume_control)
7653 - pa_volume_control_description_changed(stream->stream->own_volume_control, new_control_description);
7655 - if (stream->stream->own_mute_control)
7656 - pa_mute_control_description_changed(stream->stream->own_mute_control, new_control_description);
7658 - pa_xfree(new_control_description);
7659 + pas_stream_set_description(stream->stream, description);
7664 -static pa_hook_result_t client_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) {
7665 - struct stream *stream = userdata;
7666 - pa_client *client = call_data;
7667 - char *description;
7669 - pa_assert(stream);
7670 - pa_assert(client);
7672 - if (client != stream->client)
7673 - return PA_HOOK_OK;
7675 - description = get_stream_volume_and_mute_control_description_malloc(stream);
7677 - if (stream->stream->own_volume_control)
7678 - pa_volume_control_description_changed(stream->stream->own_volume_control, description);
7680 - if (stream->stream->own_mute_control)
7681 - pa_mute_control_description_changed(stream->stream->own_mute_control, description);
7683 - pa_xfree(description);
7685 - return PA_HOOK_OK;
7688 -static struct stream *stream_new(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
7689 - struct stream *stream;
7690 - const char *name = NULL;
7691 +static int stream_new(pa_stream_creator *creator, enum stream_type type, void *new_data, void *core_stream,
7692 + struct stream **_r) {
7693 + struct stream *stream = NULL;
7694 + pa_proplist *proplist = NULL;
7695 + pa_channel_map *channel_map = NULL;
7696 + bool volume_available = false;
7697 + pa_bvolume volume;
7698 + pa_bvolume relative_volume;
7699 + bool mute = false;
7700 + const char *stream_name = NULL;
7701 const char *description = NULL;
7702 + const char *volume_control_name = NULL;
7703 + const char *relative_volume_control_name = NULL;
7704 + const char *mute_control_name = NULL;
7705 pa_direction_t direction = PA_DIRECTION_OUTPUT;
7707 + const char *prop_key;
7708 + void *state = NULL;
7711 pa_assert(core_stream);
7714 + pa_bvolume_init_invalid(&volume);
7715 + pa_bvolume_init_invalid(&relative_volume);
7717 stream = pa_xnew0(struct stream, 1);
7718 + stream->core = creator->volume_api->core;
7719 stream->creator = creator;
7720 stream->type = type;
7723 case STREAM_TYPE_SINK_INPUT:
7724 + stream->sink_input_new_data = new_data;
7725 stream->sink_input = core_stream;
7726 - stream->client = stream->sink_input->client;
7727 - name = "sink-input-stream";
7729 - description = get_sink_input_description(stream->sink_input);
7731 - description = name;
7733 + stream->client = stream->sink_input_new_data->client;
7734 + proplist = stream->sink_input_new_data->proplist;
7735 + channel_map = &stream->sink_input_new_data->channel_map;
7736 + volume_available = stream->sink_input_new_data->volume_writable;
7738 + if (volume_available) {
7739 + if (!stream->sink_input_new_data->volume_is_set) {
7740 + pa_cvolume cvolume;
7742 + pa_cvolume_reset(&cvolume, channel_map->channels);
7743 + pa_sink_input_new_data_set_volume(stream->sink_input_new_data, &cvolume, true);
7746 + pa_bvolume_from_cvolume(&volume, &stream->sink_input_new_data->volume, channel_map);
7747 + pa_bvolume_from_cvolume(&relative_volume, &stream->sink_input_new_data->reference_ratio, channel_map);
7750 + if (!stream->sink_input_new_data->muted_is_set)
7751 + pa_sink_input_new_data_set_muted(stream->sink_input_new_data, false);
7753 + mute = stream->sink_input_new_data->muted;
7755 + stream->client = stream->sink_input->client;
7756 + proplist = stream->sink_input->proplist;
7757 + channel_map = &stream->sink_input->channel_map;
7758 + pa_bvolume_from_cvolume(&volume, &stream->sink_input->volume, channel_map);
7759 + pa_bvolume_from_cvolume(&relative_volume, &stream->sink_input->reference_ratio, channel_map);
7760 + mute = stream->sink_input->muted;
7763 + stream_name = "sink-input-stream";
7764 + volume_control_name = "sink-input-volume-control";
7765 + relative_volume_control_name = "sink-input-relative-volume-control";
7766 + mute_control_name = "sink-input-mute-control";
7768 direction = PA_DIRECTION_OUTPUT;
7770 + stream->proplist_changed_slot =
7771 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
7772 + sink_input_or_source_output_proplist_changed_cb, stream);
7773 + stream->volume_changed_slot =
7774 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
7775 + sink_input_or_source_output_volume_changed_cb, stream);
7776 + stream->reference_ratio_changed_slot =
7777 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_REFERENCE_RATIO_CHANGED], PA_HOOK_NORMAL,
7778 + sink_input_or_source_output_reference_ratio_changed_cb, stream);
7779 + stream->mute_changed_slot =
7780 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
7781 + sink_input_or_source_output_mute_changed_cb, stream);
7784 case STREAM_TYPE_SOURCE_OUTPUT:
7785 + stream->source_output_new_data = new_data;
7786 stream->source_output = core_stream;
7787 - stream->client = stream->source_output->client;
7788 - name = "source-output-stream";
7790 - description = get_source_output_description(stream->source_output);
7792 - description = name;
7794 + stream->client = stream->source_output_new_data->client;
7795 + proplist = stream->source_output_new_data->proplist;
7796 + channel_map = &stream->source_output_new_data->channel_map;
7797 + volume_available = stream->source_output_new_data->volume_writable;
7799 + if (volume_available) {
7800 + if (!stream->source_output_new_data->volume_is_set) {
7801 + pa_cvolume cvolume;
7803 + pa_cvolume_reset(&cvolume, channel_map->channels);
7804 + pa_source_output_new_data_set_volume(stream->source_output_new_data, &cvolume, true);
7807 + pa_bvolume_from_cvolume(&volume, &stream->source_output_new_data->volume, channel_map);
7808 + pa_bvolume_from_cvolume(&relative_volume, &stream->source_output_new_data->reference_ratio, channel_map);
7811 + if (!stream->source_output_new_data->muted_is_set)
7812 + pa_source_output_new_data_set_muted(stream->source_output_new_data, false);
7814 + mute = stream->source_output_new_data->muted;
7816 + stream->client = stream->source_output->client;
7817 + proplist = stream->source_output->proplist;
7818 + channel_map = &stream->source_output->channel_map;
7819 + pa_bvolume_from_cvolume(&volume, &stream->source_output->volume, channel_map);
7820 + pa_bvolume_from_cvolume(&relative_volume, &stream->source_output->reference_ratio, channel_map);
7821 + mute = stream->source_output->muted;
7824 + stream_name = "source-output-stream";
7825 + volume_control_name = "source-output-volume-control";
7826 + relative_volume_control_name = "source-output-relative-volume-control";
7827 + mute_control_name = "source-output-mute-control";
7829 direction = PA_DIRECTION_INPUT;
7833 - stream->stream = pas_stream_new(creator->volume_api, name, description, direction);
7834 - stream->stream->create_own_volume_control = stream_create_own_volume_control_cb;
7835 - stream->stream->delete_own_volume_control = stream_delete_own_volume_control_cb;
7836 - stream->stream->create_own_mute_control = stream_create_own_mute_control_cb;
7837 - stream->stream->delete_own_mute_control = stream_delete_own_mute_control_cb;
7838 - stream->stream->userdata = stream;
7839 - pas_stream_set_have_own_volume_control(stream->stream, true);
7840 - pas_stream_set_have_own_mute_control(stream->stream, true);
7843 - case STREAM_TYPE_SINK_INPUT:
7844 stream->proplist_changed_slot =
7845 - pa_hook_connect(&stream->sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
7846 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], PA_HOOK_NORMAL,
7847 sink_input_or_source_output_proplist_changed_cb, stream);
7850 - case STREAM_TYPE_SOURCE_OUTPUT:
7851 - stream->proplist_changed_slot =
7852 - pa_hook_connect(&stream->source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED],
7853 - PA_HOOK_NORMAL, sink_input_or_source_output_proplist_changed_cb, stream);
7854 + if (volume_available) {
7855 + stream->volume_changed_slot =
7856 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_VOLUME_CHANGED], PA_HOOK_NORMAL,
7857 + sink_input_or_source_output_volume_changed_cb, stream);
7858 + stream->reference_ratio_changed_slot =
7859 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_REFERENCE_RATIO_CHANGED],
7860 + PA_HOOK_NORMAL, sink_input_or_source_output_reference_ratio_changed_cb, stream);
7863 + stream->mute_changed_slot =
7864 + pa_hook_connect(&stream->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], PA_HOOK_NORMAL,
7865 + sink_input_or_source_output_mute_changed_cb, stream);
7869 - stream->client_proplist_changed_slot =
7870 - pa_hook_connect(&stream->creator->volume_api->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED],
7871 - PA_HOOK_NORMAL, client_proplist_changed_cb, stream);
7872 + r = pas_stream_new(creator->volume_api, stream_name, &stream->stream);
7878 + description = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
7880 + description = stream->stream->name;
7882 -static void stream_put(struct stream *stream) {
7883 - pa_proplist *proplist = NULL;
7884 + pas_stream_set_description(stream->stream, description);
7886 - pa_assert(stream);
7887 + while ((prop_key = pa_proplist_iterate(proplist, &state)))
7888 + pas_stream_set_property(stream->stream, prop_key, pa_proplist_gets(proplist, prop_key));
7890 - switch (stream->type) {
7891 - case STREAM_TYPE_SINK_INPUT:
7892 - proplist = stream->sink_input->proplist;
7894 + pas_stream_set_direction(stream->stream, direction);
7895 + stream->stream->userdata = stream;
7897 - case STREAM_TYPE_SOURCE_OUTPUT:
7898 - proplist = stream->source_output->proplist;
7900 + if (volume_available) {
7901 + r = pa_volume_control_new(stream->creator->volume_api, volume_control_name, false,
7902 + &stream->volume_control);
7904 + pa_volume_control_set_description(stream->volume_control, _("Volume"));
7905 + pa_volume_control_set_channel_map(stream->volume_control, channel_map);
7906 + pa_volume_control_set_volume(stream->volume_control, &volume, true, true);
7907 + pa_volume_control_set_convertible_to_dB(stream->volume_control, true);
7908 + stream->volume_control->set_volume = volume_control_set_volume_cb;
7909 + stream->volume_control->userdata = stream;
7911 + pas_stream_set_volume_control(stream->stream, stream->volume_control);
7914 + r = pa_volume_control_new(stream->creator->volume_api, relative_volume_control_name, false,
7915 + &stream->relative_volume_control);
7917 + pa_volume_control_set_description(stream->relative_volume_control, _("Relative volume"));
7918 + pa_volume_control_set_channel_map(stream->relative_volume_control, channel_map);
7919 + pa_volume_control_set_volume(stream->relative_volume_control, &relative_volume, true, true);
7920 + pa_volume_control_set_convertible_to_dB(stream->relative_volume_control, true);
7921 + pa_volume_control_set_purpose(stream->relative_volume_control, PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME,
7923 + stream->relative_volume_control->set_volume = relative_volume_control_set_volume_cb;
7924 + stream->relative_volume_control->userdata = stream;
7926 + pas_stream_set_relative_volume_control(stream->stream, stream->relative_volume_control);
7930 - pas_stream_put(stream->stream, proplist);
7932 + r = pa_mute_control_new(stream->creator->volume_api, mute_control_name, false, &stream->mute_control);
7934 + pa_mute_control_set_description(stream->mute_control, _("Mute"));
7935 + pa_mute_control_set_mute(stream->mute_control, mute);
7936 + pa_mute_control_set_purpose(stream->mute_control, PA_MUTE_CONTROL_PURPOSE_STREAM_MUTE, stream->stream);
7937 + stream->mute_control->set_mute = mute_control_set_mute_cb;
7938 + stream->mute_control->userdata = stream;
7940 -static void stream_unlink(struct stream *stream) {
7941 - pa_assert(stream);
7942 + pas_stream_set_mute_control(stream->stream, stream->mute_control);
7945 - if (stream->unlinked)
7947 + pas_stream_put(stream->stream);
7949 - stream->unlinked = true;
7950 + if (stream->volume_control)
7951 + pa_volume_control_put(stream->volume_control);
7953 - if (stream->stream)
7954 - pas_stream_unlink(stream->stream);
7955 + if (stream->relative_volume_control)
7956 + pa_volume_control_put(stream->relative_volume_control);
7958 + if (stream->mute_control)
7959 + pa_mute_control_put(stream->mute_control);
7966 + stream_free(stream);
7971 static void stream_free(struct stream *stream) {
7974 - if (!stream->unlinked)
7975 - stream_unlink(stream);
7976 + if (stream->mute_changed_slot)
7977 + pa_hook_slot_free(stream->mute_changed_slot);
7979 + if (stream->reference_ratio_changed_slot)
7980 + pa_hook_slot_free(stream->reference_ratio_changed_slot);
7982 - if (stream->client_proplist_changed_slot)
7983 - pa_hook_slot_free(stream->client_proplist_changed_slot);
7984 + if (stream->volume_changed_slot)
7985 + pa_hook_slot_free(stream->volume_changed_slot);
7987 if (stream->proplist_changed_slot)
7988 pa_hook_slot_free(stream->proplist_changed_slot);
7990 + if (stream->mute_control)
7991 + pa_mute_control_free(stream->mute_control);
7993 + if (stream->relative_volume_control)
7994 + pa_volume_control_free(stream->relative_volume_control);
7996 + if (stream->volume_control)
7997 + pa_volume_control_free(stream->volume_control);
8000 pas_stream_free(stream->stream);
8005 -static void create_stream(pa_stream_creator *creator, enum stream_type type, void *core_stream) {
8006 +static pa_hook_result_t sink_input_fixate_cb(void *hook_data, void *call_data, void *userdata) {
8007 + pa_stream_creator *creator = userdata;
8008 + pa_sink_input_new_data *data = call_data;
8010 struct stream *stream;
8013 - pa_assert(core_stream);
8016 - stream = stream_new(creator, type, core_stream);
8017 - pa_hashmap_put(creator->streams, core_stream, stream);
8018 - stream_put(stream);
8021 -static pa_hook_result_t sink_input_put_cb(void *hook_data, void *call_data, void *userdata) {
8022 - pa_stream_creator *creator = userdata;
8023 - pa_sink_input *input = call_data;
8025 - pa_assert(creator);
8027 + r = stream_new(creator, STREAM_TYPE_SINK_INPUT, data, data->sink_input, &stream);
8029 + return PA_HOOK_OK;
8031 - create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
8032 + pa_hashmap_put(creator->streams, stream->sink_input, stream);
8036 @@ -615,14 +638,20 @@ static pa_hook_result_t sink_input_unlink_cb(void *hook_data, void *call_data, v
8040 -static pa_hook_result_t source_output_put_cb(void *hook_data, void *call_data, void *userdata) {
8041 +static pa_hook_result_t source_output_fixate_cb(void *hook_data, void *call_data, void *userdata) {
8042 pa_stream_creator *creator = userdata;
8043 - pa_source_output *output = call_data;
8044 + pa_source_output_new_data *data = call_data;
8046 + struct stream *stream;
8049 - pa_assert(output);
8052 + r = stream_new(creator, STREAM_TYPE_SOURCE_OUTPUT, data, data->source_output, &stream);
8054 + return PA_HOOK_OK;
8056 - create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
8057 + pa_hashmap_put(creator->streams, stream->source_output, stream);
8061 @@ -644,26 +673,34 @@ pa_stream_creator *pa_stream_creator_new(pa_volume_api *api) {
8063 pa_sink_input *input;
8064 pa_source_output *output;
8066 + struct stream *stream;
8070 creator = pa_xnew0(pa_stream_creator, 1);
8071 creator->volume_api = api;
8072 creator->streams = pa_hashmap_new_full(NULL, NULL, NULL, (pa_free_cb_t) stream_free);
8073 - creator->sink_input_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_NORMAL,
8074 - sink_input_put_cb, creator);
8075 + creator->sink_input_fixate_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL,
8076 + sink_input_fixate_cb, creator);
8077 creator->sink_input_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_NORMAL,
8078 sink_input_unlink_cb, creator);
8079 - creator->source_output_put_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
8080 - source_output_put_cb, creator);
8081 + creator->source_output_fixate_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL,
8082 + source_output_fixate_cb, creator);
8083 creator->source_output_unlink_slot = pa_hook_connect(&api->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL,
8084 source_output_unlink_cb, creator);
8086 - PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx)
8087 - create_stream(creator, STREAM_TYPE_SINK_INPUT, input);
8088 + PA_IDXSET_FOREACH(input, api->core->sink_inputs, idx) {
8089 + r = stream_new(creator, STREAM_TYPE_SINK_INPUT, NULL, input, &stream);
8091 + pa_hashmap_put(creator->streams, stream->sink_input, stream);
8094 - PA_IDXSET_FOREACH(output, api->core->source_outputs, idx)
8095 - create_stream(creator, STREAM_TYPE_SOURCE_OUTPUT, output);
8096 + PA_IDXSET_FOREACH(output, api->core->source_outputs, idx) {
8097 + r = stream_new(creator, STREAM_TYPE_SOURCE_OUTPUT, NULL, output, &stream);
8099 + pa_hashmap_put(creator->streams, stream->source_output, stream);
8104 @@ -677,14 +714,14 @@ void pa_stream_creator_free(pa_stream_creator *creator) {
8105 if (creator->source_output_unlink_slot)
8106 pa_hook_slot_free(creator->source_output_unlink_slot);
8108 - if (creator->source_output_put_slot)
8109 - pa_hook_slot_free(creator->source_output_put_slot);
8110 + if (creator->source_output_fixate_slot)
8111 + pa_hook_slot_free(creator->source_output_fixate_slot);
8113 if (creator->sink_input_unlink_slot)
8114 pa_hook_slot_free(creator->sink_input_unlink_slot);
8116 - if (creator->sink_input_put_slot)
8117 - pa_hook_slot_free(creator->sink_input_put_slot);
8118 + if (creator->sink_input_fixate_slot)
8119 + pa_hook_slot_free(creator->sink_input_fixate_slot);
8121 if (creator->streams)
8122 pa_hashmap_free(creator->streams);
8123 diff --git a/src/modules/volume-api/volume-api.c b/src/modules/volume-api/volume-api.c
8124 index 9abea7e..4a8a2e6 100644
8125 --- a/src/modules/volume-api/volume-api.c
8126 +++ b/src/modules/volume-api/volume-api.c
8128 #include "volume-api.h"
8130 #include <modules/volume-api/audio-group.h>
8131 -#include <modules/volume-api/binding.h>
8132 #include <modules/volume-api/device.h>
8133 #include <modules/volume-api/device-creator.h>
8134 +#include <modules/volume-api/inidb.h>
8135 #include <modules/volume-api/sstream.h>
8136 #include <modules/volume-api/stream-creator.h>
8137 #include <modules/volume-api/volume-control.h>
8139 #include <pulsecore/core-util.h>
8140 +#include <pulsecore/namereg.h>
8141 #include <pulsecore/shared.h>
8143 +#define CONTROL_DB_TABLE_NAME_VOLUME_CONTROL "VolumeControl"
8144 +#define CONTROL_DB_TABLE_NAME_MUTE_CONTROL "MuteControl"
8146 static pa_volume_api *volume_api_new(pa_core *core);
8147 static void volume_api_free(pa_volume_api *api);
8149 @@ -76,44 +80,209 @@ void pa_volume_api_unref(pa_volume_api *api) {
8153 -void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
8156 +static int control_db_get_volume_control_cb(pa_inidb *db, const char *name, void **_r) {
8157 + pa_volume_api *api;
8158 + pa_volume_control *control;
8164 + api = pa_inidb_get_userdata(db);
8166 - pa_assert_se(pa_hashmap_put(api->binding_target_types, type->name, type) >= 0);
8167 + control = pa_hashmap_get(api->volume_controls_from_db, name);
8171 - pa_log_debug("Added binding target type %s.", type->name);
8172 + r = pa_volume_control_new(api, name, true, &control);
8176 - pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED], type);
8177 + pa_hashmap_put(api->volume_controls_from_db, (void *) control->name, control);
8184 -void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type) {
8187 +static int control_db_parse_volume_control_description_cb(pa_inidb *db, const char *value, void *object) {
8188 + pa_volume_control *control = object;
8192 + pa_assert(control);
8194 - pa_log_debug("Removing binding target type %s.", type->name);
8195 + pa_volume_control_set_description(control, value);
8197 - pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED], type);
8201 +static int control_db_parse_volume_control_volume_cb(pa_inidb *db, const char *value, void *object) {
8202 + pa_volume_control *control = object;
8204 + pa_bvolume bvolume;
8208 + pa_assert(control);
8210 + r = pa_atou(value, &bvolume.volume);
8212 + return -PA_ERR_INVALID;
8214 + if (!PA_VOLUME_IS_VALID(bvolume.volume))
8215 + return -PA_ERR_INVALID;
8217 - pa_assert_se(pa_hashmap_remove(api->binding_target_types, type->name));
8218 + pa_volume_control_set_volume(control, &bvolume, true, false);
8223 -static void create_builtin_binding_target_types(pa_volume_api *api) {
8224 - pa_binding_target_type *type;
8225 +static int control_db_parse_volume_control_balance_cb(pa_inidb *db, const char *value, void *object) {
8226 + pa_volume_control *control = object;
8228 + pa_bvolume bvolume;
8233 + pa_assert(control);
8235 + r = pa_bvolume_parse_balance(value, &bvolume);
8237 + return -PA_ERR_INVALID;
8239 + pa_volume_control_set_channel_map(control, &bvolume.channel_map);
8240 + pa_volume_control_set_volume(control, &bvolume, false, true);
8245 +static int control_db_parse_volume_control_convertible_to_dB_cb(pa_inidb *db, const char *value, void *object) {
8246 + pa_volume_control *control = object;
8251 + pa_assert(control);
8253 + r = pa_parse_boolean(value);
8255 + return -PA_ERR_INVALID;
8257 + pa_volume_control_set_convertible_to_dB(control, r);
8262 +static int control_db_get_mute_control_cb(pa_inidb *db, const char *name, void **_r) {
8263 + pa_volume_api *api;
8264 + pa_mute_control *control;
8270 + api = pa_inidb_get_userdata(db);
8272 + control = pa_hashmap_get(api->mute_controls_from_db, name);
8276 + r = pa_mute_control_new(api, name, true, &control);
8280 + pa_hashmap_put(api->mute_controls_from_db, (void *) control->name, control);
8287 +static int control_db_parse_mute_control_description_cb(pa_inidb *db, const char *value, void *object) {
8288 + pa_mute_control *control = object;
8290 - type = pa_audio_group_create_binding_target_type(api);
8291 - pa_volume_api_add_binding_target_type(api, type);
8294 + pa_assert(control);
8296 + pa_mute_control_set_description(control, value);
8301 -static void delete_builtin_binding_target_types(pa_volume_api *api) {
8302 - pa_binding_target_type *type;
8303 +static int control_db_parse_mute_control_mute_cb(pa_inidb *db, const char *value, void *object) {
8304 + pa_mute_control *control = object;
8309 + pa_assert(control);
8311 + mute = pa_parse_boolean(value);
8313 + return -PA_ERR_INVALID;
8315 + pa_mute_control_set_mute(control, mute);
8320 +static void create_control_db(pa_volume_api *api) {
8321 + pa_volume_control *volume_control;
8322 + pa_mute_control *mute_control;
8325 + pa_assert(!api->control_db.db);
8327 + api->control_db.db = pa_inidb_new(api->core, "controls", api);
8329 + api->control_db.volume_controls = pa_inidb_add_table(api->control_db.db, CONTROL_DB_TABLE_NAME_VOLUME_CONTROL,
8330 + control_db_get_volume_control_cb);
8331 + pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION,
8332 + control_db_parse_volume_control_description_cb);
8333 + pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME,
8334 + control_db_parse_volume_control_volume_cb);
8335 + pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE,
8336 + control_db_parse_volume_control_balance_cb);
8337 + pa_inidb_table_add_column(api->control_db.volume_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB,
8338 + control_db_parse_volume_control_convertible_to_dB_cb);
8340 + api->control_db.mute_controls = pa_inidb_add_table(api->control_db.db, CONTROL_DB_TABLE_NAME_MUTE_CONTROL,
8341 + control_db_get_mute_control_cb);
8342 + pa_inidb_table_add_column(api->control_db.mute_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION,
8343 + control_db_parse_mute_control_description_cb);
8344 + pa_inidb_table_add_column(api->control_db.mute_controls, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE,
8345 + control_db_parse_mute_control_mute_cb);
8347 + api->volume_controls_from_db = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8348 + api->mute_controls_from_db = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8350 + pa_inidb_load(api->control_db.db);
8352 + while ((volume_control = pa_hashmap_steal_first(api->volume_controls_from_db)))
8353 + pa_volume_control_put(volume_control);
8355 + pa_hashmap_free(api->volume_controls_from_db);
8356 + api->volume_controls_from_db = NULL;
8358 + while ((mute_control = pa_hashmap_steal_first(api->mute_controls_from_db)))
8359 + pa_mute_control_put(mute_control);
8361 + pa_hashmap_free(api->mute_controls_from_db);
8362 + api->mute_controls_from_db = NULL;
8365 +static void delete_control_db(pa_volume_api *api) {
8368 - type = pa_hashmap_get(api->binding_target_types, PA_AUDIO_GROUP_BINDING_TARGET_TYPE);
8369 - pa_volume_api_remove_binding_target_type(api, type);
8370 + if (!api->control_db.db)
8373 + pa_inidb_free(api->control_db.db);
8374 + api->control_db.mute_controls = NULL;
8375 + api->control_db.volume_controls = NULL;
8376 + api->control_db.db = NULL;
8379 static void create_objects_defer_event_cb(pa_mainloop_api *mainloop_api, pa_defer_event *event, void *userdata) {
8380 @@ -138,7 +307,6 @@ static pa_volume_api *volume_api_new(pa_core *core) {
8381 api = pa_xnew0(pa_volume_api, 1);
8384 - api->binding_target_types = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8385 api->names = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
8386 api->volume_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8387 api->mute_controls = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
8388 @@ -149,7 +317,7 @@ static pa_volume_api *volume_api_new(pa_core *core) {
8389 for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
8390 pa_hook_init(&api->hooks[i], api);
8392 - create_builtin_binding_target_types(api);
8393 + create_control_db(api);
8395 /* We delay the object creation to ensure that policy modules have a chance
8396 * to affect the initialization of the objects. If we created the objects
8397 @@ -170,6 +338,9 @@ static void volume_api_free(pa_volume_api *api) {
8399 pa_log_debug("Freeing the pa_volume_api object.");
8401 + pa_assert(!api->mute_controls_from_db);
8402 + pa_assert(!api->volume_controls_from_db);
8404 if (api->stream_creator)
8405 pa_stream_creator_free(api->stream_creator);
8407 @@ -179,8 +350,7 @@ static void volume_api_free(pa_volume_api *api) {
8408 if (api->create_objects_defer_event)
8409 api->core->mainloop->defer_free(api->create_objects_defer_event);
8411 - if (api->binding_target_types)
8412 - delete_builtin_binding_target_types(api);
8413 + delete_control_db(api);
8415 for (i = 0; i < PA_VOLUME_API_HOOK_MAX; i++)
8416 pa_hook_done(&api->hooks[i]);
8417 @@ -201,12 +371,24 @@ static void volume_api_free(pa_volume_api *api) {
8420 if (api->mute_controls) {
8421 - pa_assert(pa_hashmap_isempty(api->mute_controls));
8422 + pa_mute_control *control;
8424 + while ((control = pa_hashmap_first(api->mute_controls))) {
8425 + pa_assert(!control->present);
8426 + pa_mute_control_free(control);
8429 pa_hashmap_free(api->mute_controls);
8432 if (api->volume_controls) {
8433 - pa_assert(pa_hashmap_isempty(api->volume_controls));
8434 + pa_volume_control *control;
8436 + while ((control = pa_hashmap_first(api->volume_controls))) {
8437 + pa_assert(!control->present);
8438 + pa_volume_control_free(control);
8441 pa_hashmap_free(api->volume_controls);
8444 @@ -215,11 +397,6 @@ static void volume_api_free(pa_volume_api *api) {
8445 pa_hashmap_free(api->names);
8448 - if (api->binding_target_types) {
8449 - pa_assert(pa_hashmap_isempty(api->binding_target_types));
8450 - pa_hashmap_free(api->binding_target_types);
8456 @@ -231,19 +408,24 @@ int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name,
8457 pa_assert(requested_name);
8458 pa_assert(registered_name);
8460 + if (!pa_namereg_is_valid_name(requested_name)) {
8461 + pa_log("Invalid name: \"%s\"", requested_name);
8462 + return -PA_ERR_INVALID;
8465 n = pa_xstrdup(requested_name);
8467 if (pa_hashmap_put(api->names, n, n) < 0) {
8472 if (fail_if_already_registered) {
8474 pa_log("Name %s already registered.", requested_name);
8475 return -PA_ERR_EXIST;
8481 n = pa_sprintf_malloc("%s.%u", requested_name, i);
8482 } while (pa_hashmap_put(api->names, n, n) < 0);
8483 @@ -271,38 +453,6 @@ uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api) {
8487 -static void set_main_output_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
8488 - pa_volume_control *old_control;
8492 - old_control = api->main_output_volume_control;
8494 - if (control == old_control)
8497 - api->main_output_volume_control = control;
8498 - pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8499 - control ? control->name : "(unset)");
8500 - pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
8503 -static void set_main_input_volume_control_internal(pa_volume_api *api, pa_volume_control *control) {
8504 - pa_volume_control *old_control;
8508 - old_control = api->main_input_volume_control;
8510 - if (control == old_control)
8513 - api->main_input_volume_control = control;
8514 - pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8515 - control ? control->name : "(unset)");
8516 - pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
8519 void pa_volume_api_add_volume_control(pa_volume_api *api, pa_volume_control *control) {
8522 @@ -318,10 +468,10 @@ int pa_volume_api_remove_volume_control(pa_volume_api *api, pa_volume_control *c
8525 if (control == api->main_output_volume_control)
8526 - set_main_output_volume_control_internal(api, NULL);
8527 + pa_volume_api_set_main_output_volume_control(api, NULL);
8529 if (control == api->main_input_volume_control)
8530 - set_main_input_volume_control_internal(api, NULL);
8531 + pa_volume_api_set_main_input_volume_control(api, NULL);
8535 @@ -350,38 +500,6 @@ uint32_t pa_volume_api_allocate_mute_control_index(pa_volume_api *api) {
8539 -static void set_main_output_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
8540 - pa_mute_control *old_control;
8544 - old_control = api->main_output_mute_control;
8546 - if (control == old_control)
8549 - api->main_output_mute_control = control;
8550 - pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8551 - control ? control->name : "(unset)");
8552 - pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
8555 -static void set_main_input_mute_control_internal(pa_volume_api *api, pa_mute_control *control) {
8556 - pa_mute_control *old_control;
8560 - old_control = api->main_input_mute_control;
8562 - if (control == old_control)
8565 - api->main_input_mute_control = control;
8566 - pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8567 - control ? control->name : "(unset)");
8568 - pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
8571 void pa_volume_api_add_mute_control(pa_volume_api *api, pa_mute_control *control) {
8574 @@ -397,10 +515,10 @@ int pa_volume_api_remove_mute_control(pa_volume_api *api, pa_mute_control *contr
8577 if (control == api->main_output_mute_control)
8578 - set_main_output_mute_control_internal(api, NULL);
8579 + pa_volume_api_set_main_output_mute_control(api, NULL);
8581 if (control == api->main_input_mute_control)
8582 - set_main_input_mute_control_internal(api, NULL);
8583 + pa_volume_api_set_main_input_mute_control(api, NULL);
8587 @@ -543,105 +661,73 @@ pa_audio_group *pa_volume_api_get_audio_group_by_index(pa_volume_api *api, uint3
8590 void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_control *control) {
8591 + pa_volume_control *old_control;
8595 - if (api->main_output_volume_control_binding) {
8596 - pa_binding_free(api->main_output_volume_control_binding);
8597 - api->main_output_volume_control_binding = NULL;
8599 + old_control = api->main_output_volume_control;
8601 - set_main_output_volume_control_internal(api, control);
8603 + if (control == old_control)
8606 -void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
8608 + api->main_output_volume_control = control;
8610 - if (api->main_input_volume_control_binding) {
8611 - pa_binding_free(api->main_input_volume_control_binding);
8612 - api->main_input_volume_control_binding = NULL;
8614 + pa_log_debug("Main output volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8615 + control ? control->name : "(unset)");
8617 - set_main_input_volume_control_internal(api, control);
8618 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED], api);
8621 -void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
8622 +void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control) {
8623 + pa_volume_control *old_control;
8627 - if (api->main_output_mute_control_binding) {
8628 - pa_binding_free(api->main_output_mute_control_binding);
8629 - api->main_output_mute_control_binding = NULL;
8631 + old_control = api->main_input_volume_control;
8633 - set_main_output_mute_control_internal(api, control);
8635 + if (control == old_control)
8638 -void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
8640 + api->main_input_volume_control = control;
8642 - if (api->main_input_mute_control_binding) {
8643 - pa_binding_free(api->main_input_mute_control_binding);
8644 - api->main_input_mute_control_binding = NULL;
8646 + pa_log_debug("Main input volume control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8647 + control ? control->name : "(unset)");
8649 - set_main_input_mute_control_internal(api, control);
8650 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_VOLUME_CONTROL_CHANGED], api);
8653 -void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8654 - pa_binding_owner_info owner_info = {
8656 - .set_value = (pa_binding_set_value_cb_t) set_main_output_volume_control_internal,
8658 +void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control) {
8659 + pa_mute_control *old_control;
8662 - pa_assert(target_info);
8664 - if (api->main_output_volume_control_binding)
8665 - pa_binding_free(api->main_output_volume_control_binding);
8667 - api->main_output_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
8669 + old_control = api->main_output_mute_control;
8671 -void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8672 - pa_binding_owner_info owner_info = {
8674 - .set_value = (pa_binding_set_value_cb_t) set_main_input_volume_control_internal,
8676 + if (control == old_control)
8680 - pa_assert(target_info);
8681 + api->main_output_mute_control = control;
8683 - if (api->main_input_volume_control_binding)
8684 - pa_binding_free(api->main_input_volume_control_binding);
8685 + pa_log_debug("Main output mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8686 + control ? control->name : "(unset)");
8688 - api->main_input_volume_control_binding = pa_binding_new(api, &owner_info, target_info);
8689 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_OUTPUT_MUTE_CONTROL_CHANGED], api);
8692 -void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8693 - pa_binding_owner_info owner_info = {
8695 - .set_value = (pa_binding_set_value_cb_t) set_main_output_mute_control_internal,
8697 +void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control) {
8698 + pa_mute_control *old_control;
8701 - pa_assert(target_info);
8703 - if (api->main_output_mute_control_binding)
8704 - pa_binding_free(api->main_output_mute_control_binding);
8706 - api->main_output_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
8708 + old_control = api->main_input_mute_control;
8710 -void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info) {
8711 - pa_binding_owner_info owner_info = {
8713 - .set_value = (pa_binding_set_value_cb_t) set_main_input_mute_control_internal,
8715 + if (control == old_control)
8719 - pa_assert(target_info);
8720 + api->main_input_mute_control = control;
8722 - if (api->main_input_mute_control_binding)
8723 - pa_binding_free(api->main_input_mute_control_binding);
8724 + pa_log_debug("Main input mute control changed from %s to %s.", old_control ? old_control->name : "(unset)",
8725 + control ? control->name : "(unset)");
8727 - api->main_input_mute_control_binding = pa_binding_new(api, &owner_info, target_info);
8728 + pa_hook_fire(&api->hooks[PA_VOLUME_API_HOOK_MAIN_INPUT_MUTE_CONTROL_CHANGED], api);
8730 diff --git a/src/modules/volume-api/volume-api.h b/src/modules/volume-api/volume-api.h
8731 index 73a1410..f99182a 100644
8732 --- a/src/modules/volume-api/volume-api.h
8733 +++ b/src/modules/volume-api/volume-api.h
8736 #include <pulsecore/core.h>
8738 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION "description"
8739 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME "volume"
8740 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE "balance"
8741 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB "convertible-to-dB"
8742 +#define PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_MUTE "mute"
8744 typedef struct pa_volume_api pa_volume_api;
8746 /* Avoid circular dependencies... */
8747 typedef struct pa_audio_group pa_audio_group;
8748 -typedef struct pa_binding pa_binding;
8749 -typedef struct pa_binding_target_info pa_binding_target_info;
8750 -typedef struct pa_binding_target_type pa_binding_target_type;
8751 typedef struct pa_device pa_device;
8752 typedef struct pa_device_creator pa_device_creator;
8753 +typedef struct pa_inidb pa_inidb;
8754 +typedef struct pa_inidb_table pa_inidb_table;
8755 typedef struct pa_mute_control pa_mute_control;
8756 typedef struct pas_stream pas_stream;
8757 typedef struct pa_stream_creator pa_stream_creator;
8758 typedef struct pa_volume_control pa_volume_control;
8761 - PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_ADDED,
8762 - PA_VOLUME_API_HOOK_BINDING_TARGET_TYPE_REMOVED,
8763 + /* This is fired after the volume control implementation has done its part
8764 + * of the volume control initialization, but before policy modules have
8765 + * done their part of the initialization. Hook users are expected to not
8766 + * modify the volume control state in this hook. */
8767 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED,
8769 + /* Policy modules can use this hook to initialize the volume control
8770 + * volume. This is fired before PUT. If a policy module sets the volume, it
8771 + * should return PA_HOOK_STOP to prevent lower-priority policy modules from
8772 + * modifying the volume. */
8773 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME,
8775 PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT,
8776 PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK,
8777 PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED,
8778 PA_VOLUME_API_HOOK_VOLUME_CONTROL_VOLUME_CHANGED,
8779 + PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED,
8781 + /* This is fired after the mute control implementation has done its part of
8782 + * the mute control initialization, but before policy modules have done
8783 + * their part of the initialization. Hook users are expected to not modify
8784 + * the mute control state in this hook. */
8785 + PA_VOLUME_API_HOOK_MUTE_CONTROL_IMPLEMENTATION_INITIALIZED,
8787 + /* Policy modules can use this hook to initialize the mute control mute.
8788 + * This is fired before PUT. If a policy module sets the mute, it should
8789 + * return PA_HOOK_STOP to prevent lower-priority policy modules from
8790 + * modifying the mute. */
8791 + PA_VOLUME_API_HOOK_MUTE_CONTROL_SET_INITIAL_MUTE,
8793 PA_VOLUME_API_HOOK_MUTE_CONTROL_PUT,
8794 PA_VOLUME_API_HOOK_MUTE_CONTROL_UNLINK,
8795 PA_VOLUME_API_HOOK_MUTE_CONTROL_DESCRIPTION_CHANGED,
8796 @@ -54,26 +83,16 @@ enum {
8797 PA_VOLUME_API_HOOK_DEVICE_DESCRIPTION_CHANGED,
8798 PA_VOLUME_API_HOOK_DEVICE_VOLUME_CONTROL_CHANGED,
8799 PA_VOLUME_API_HOOK_DEVICE_MUTE_CONTROL_CHANGED,
8801 - /* Policy modules can use this to set the initial volume control for a
8802 - * stream. The hook callback should use pas_stream_set_volume_control() to
8803 - * set the volume control. The hook callback should not do anything if
8804 - * stream->volume_control is already non-NULL. */
8805 - PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_VOLUME_CONTROL,
8807 - /* Policy modules can use this to set the initial mute control for a
8808 - * stream. The hook callback should use pas_stream_set_mute_control() to
8809 - * set the mute control. The hook callback should not do anything if
8810 - * stream->mute_control is already non-NULL. */
8811 - PA_VOLUME_API_HOOK_STREAM_SET_INITIAL_MUTE_CONTROL,
8813 PA_VOLUME_API_HOOK_STREAM_PUT,
8814 PA_VOLUME_API_HOOK_STREAM_UNLINK,
8815 PA_VOLUME_API_HOOK_STREAM_DESCRIPTION_CHANGED,
8816 + PA_VOLUME_API_HOOK_STREAM_PROPLIST_CHANGED,
8817 PA_VOLUME_API_HOOK_STREAM_VOLUME_CONTROL_CHANGED,
8818 + PA_VOLUME_API_HOOK_STREAM_RELATIVE_VOLUME_CONTROL_CHANGED,
8819 PA_VOLUME_API_HOOK_STREAM_MUTE_CONTROL_CHANGED,
8820 PA_VOLUME_API_HOOK_AUDIO_GROUP_PUT,
8821 PA_VOLUME_API_HOOK_AUDIO_GROUP_UNLINK,
8822 + PA_VOLUME_API_HOOK_AUDIO_GROUP_DESCRIPTION_CHANGED,
8823 PA_VOLUME_API_HOOK_AUDIO_GROUP_VOLUME_CONTROL_CHANGED,
8824 PA_VOLUME_API_HOOK_AUDIO_GROUP_MUTE_CONTROL_CHANGED,
8825 PA_VOLUME_API_HOOK_MAIN_OUTPUT_VOLUME_CONTROL_CHANGED,
8826 @@ -86,7 +105,6 @@ enum {
8827 struct pa_volume_api {
8830 - pa_hashmap *binding_target_types; /* name -> pa_binding_target_type */
8831 pa_hashmap *names; /* object name -> object name (hashmap-as-a-set) */
8832 pa_hashmap *volume_controls; /* name -> pa_volume_control */
8833 pa_hashmap *mute_controls; /* name -> pa_mute_control */
8834 @@ -103,27 +121,28 @@ struct pa_volume_api {
8835 uint32_t next_device_index;
8836 uint32_t next_stream_index;
8837 uint32_t next_audio_group_index;
8838 - pa_binding *main_output_volume_control_binding;
8839 - pa_binding *main_input_volume_control_binding;
8840 - pa_binding *main_output_mute_control_binding;
8841 - pa_binding *main_input_mute_control_binding;
8842 pa_hook hooks[PA_VOLUME_API_HOOK_MAX];
8846 + pa_inidb_table *volume_controls;
8847 + pa_inidb_table *mute_controls;
8850 pa_defer_event *create_objects_defer_event;
8851 pa_device_creator *device_creator;
8852 pa_stream_creator *stream_creator;
8854 + pa_hashmap *volume_controls_from_db; /* control name -> pa_volume_control, only used during initialization. */
8855 + pa_hashmap *mute_controls_from_db; /* control name -> pa_mute_control, only used during initialization. */
8858 pa_volume_api *pa_volume_api_get(pa_core *core);
8859 pa_volume_api *pa_volume_api_ref(pa_volume_api *api);
8860 void pa_volume_api_unref(pa_volume_api *api);
8862 -void pa_volume_api_add_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
8863 -void pa_volume_api_remove_binding_target_type(pa_volume_api *api, pa_binding_target_type *type);
8865 -/* If fail_if_already_registered is false, this function never fails. */
8866 int pa_volume_api_register_name(pa_volume_api *api, const char *requested_name, bool fail_if_already_registered,
8867 const char **registered_name);
8869 void pa_volume_api_unregister_name(pa_volume_api *api, const char *name);
8871 uint32_t pa_volume_api_allocate_volume_control_index(pa_volume_api *api);
8872 @@ -155,9 +174,5 @@ void pa_volume_api_set_main_output_volume_control(pa_volume_api *api, pa_volume_
8873 void pa_volume_api_set_main_input_volume_control(pa_volume_api *api, pa_volume_control *control);
8874 void pa_volume_api_set_main_output_mute_control(pa_volume_api *api, pa_mute_control *control);
8875 void pa_volume_api_set_main_input_mute_control(pa_volume_api *api, pa_mute_control *control);
8876 -void pa_volume_api_bind_main_output_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
8877 -void pa_volume_api_bind_main_input_volume_control(pa_volume_api *api, pa_binding_target_info *target_info);
8878 -void pa_volume_api_bind_main_output_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
8879 -void pa_volume_api_bind_main_input_mute_control(pa_volume_api *api, pa_binding_target_info *target_info);
8882 diff --git a/src/modules/volume-api/volume-control.c b/src/modules/volume-api/volume-control.c
8883 index c7f5dbb..bf4db71 100644
8884 --- a/src/modules/volume-api/volume-control.c
8885 +++ b/src/modules/volume-api/volume-control.c
8888 #include <modules/volume-api/audio-group.h>
8889 #include <modules/volume-api/device.h>
8890 +#include <modules/volume-api/inidb.h>
8891 #include <modules/volume-api/sstream.h>
8893 #include <pulsecore/core-util.h>
8895 -pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
8896 - bool channel_map_is_writable) {
8897 - pa_volume_control *control;
8898 +int pa_volume_control_new(pa_volume_api *api, const char *name, bool persistent, pa_volume_control **_r) {
8899 + pa_volume_control *control = NULL;
8904 - pa_assert(description);
8907 control = pa_xnew0(pa_volume_control, 1);
8908 control->volume_api = api;
8909 control->index = pa_volume_api_allocate_volume_control_index(api);
8910 - pa_assert_se(pa_volume_api_register_name(api, name, false, &control->name) >= 0);
8911 - control->description = pa_xstrdup(description);
8913 + r = pa_volume_api_register_name(api, name, persistent, &control->name);
8917 + control->description = pa_xstrdup(control->name);
8918 control->proplist = pa_proplist_new();
8919 - pa_bvolume_init_invalid(&control->volume);
8920 - control->convertible_to_dB = convertible_to_dB;
8921 - control->channel_map_is_writable = channel_map_is_writable;
8922 + pa_bvolume_init_mono(&control->volume, PA_VOLUME_NORM);
8923 + control->present = !persistent;
8924 + control->persistent = persistent;
8925 + control->purpose = PA_VOLUME_CONTROL_PURPOSE_OTHER;
8926 control->devices = pa_hashmap_new(NULL, NULL);
8927 control->default_for_devices = pa_hashmap_new(NULL, NULL);
8928 - control->streams = pa_hashmap_new(NULL, NULL);
8929 - control->audio_groups = pa_hashmap_new(NULL, NULL);
8933 + pa_inidb_row *row;
8935 + row = pa_inidb_table_add_row(api->control_db.volume_controls, control->name);
8936 + control->db_cells.description = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_DESCRIPTION);
8937 + control->db_cells.volume = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_VOLUME);
8938 + control->db_cells.balance = pa_inidb_row_get_cell(row, PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_BALANCE);
8939 + control->db_cells.convertible_to_dB = pa_inidb_row_get_cell(row,
8940 + PA_VOLUME_API_CONTROL_DB_COLUMN_NAME_CONVERTIBLE_TO_DB);
8948 + pa_volume_control_free(control);
8953 -void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
8954 - pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb) {
8955 +void pa_volume_control_put(pa_volume_control *control) {
8956 const char *prop_key;
8958 char volume_str[PA_VOLUME_SNPRINT_VERBOSE_MAX];
8959 char balance_str[PA_BVOLUME_SNPRINT_BALANCE_MAX];
8962 - pa_assert((initial_volume && pa_bvolume_valid(initial_volume, true, true)) || control->set_volume);
8963 - pa_assert((initial_volume && pa_channel_map_valid(&initial_volume->channel_map)) || control->channel_map_is_writable);
8964 - pa_assert(set_initial_volume_cb || !control->set_volume);
8966 - if (initial_volume && pa_bvolume_valid(initial_volume, true, false))
8967 - control->volume.volume = initial_volume->volume;
8969 - control->volume.volume = PA_VOLUME_NORM / 3;
8971 - if (initial_volume && pa_bvolume_valid(initial_volume, false, true))
8972 - pa_bvolume_copy_balance(&control->volume, initial_volume);
8973 - else if (initial_volume && pa_channel_map_valid(&initial_volume->channel_map))
8974 - pa_bvolume_reset_balance(&control->volume, &initial_volume->channel_map);
8976 - pa_channel_map_init_mono(&control->volume.channel_map);
8977 - pa_bvolume_reset_balance(&control->volume, &control->volume.channel_map);
8979 + pa_assert(control->set_volume || !control->present);
8981 - if (set_initial_volume_cb)
8982 - set_initial_volume_cb(control);
8983 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_IMPLEMENTATION_INITIALIZED], control);
8984 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_SET_INITIAL_VOLUME], control);
8986 - pa_volume_api_add_volume_control(control->volume_api, control);
8987 + if (control->set_volume) {
8988 + control->set_volume_in_progress = true;
8989 + control->set_volume(control, &control->volume, &control->volume, true, true);
8990 + control->set_volume_in_progress = false;
8993 + pa_volume_api_add_volume_control(control->volume_api, control);
8994 control->linked = true;
8996 pa_log_debug("Created volume control #%u.", control->index);
8997 pa_log_debug(" Name: %s", control->name);
8998 pa_log_debug(" Description: %s", control->description);
8999 + pa_log_debug(" Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
9000 + control->convertible_to_dB));
9001 + pa_log_debug(" Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
9002 + pa_log_debug(" Present: %s", pa_yes_no(control->present));
9003 + pa_log_debug(" Persistent: %s", pa_yes_no(control->persistent));
9004 pa_log_debug(" Properties:");
9006 while ((prop_key = pa_proplist_iterate(control->proplist, &state)))
9007 pa_log_debug(" %s = %s", prop_key, pa_strnull(pa_proplist_gets(control->proplist, prop_key)));
9009 - pa_log_debug(" Volume: %s", pa_volume_snprint_verbose(volume_str, sizeof(volume_str), control->volume.volume,
9010 - control->convertible_to_dB));
9011 - pa_log_debug(" Balance: %s", pa_bvolume_snprint_balance(balance_str, sizeof(balance_str), &control->volume));
9012 - pa_log_debug(" Channel map is writable: %s", pa_yes_no(control->channel_map_is_writable));
9014 pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_PUT], control);
9017 void pa_volume_control_unlink(pa_volume_control *control) {
9018 - pa_audio_group *group;
9020 - pas_stream *stream;
9024 @@ -122,15 +130,9 @@ void pa_volume_control_unlink(pa_volume_control *control) {
9025 pa_log_debug("Unlinking volume control %s.", control->name);
9027 if (control->linked)
9028 - pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
9030 - pa_volume_api_remove_volume_control(control->volume_api, control);
9032 - while ((group = pa_hashmap_first(control->audio_groups)))
9033 - pa_audio_group_set_volume_control(group, NULL);
9034 + pa_volume_api_remove_volume_control(control->volume_api, control);
9036 - while ((stream = pa_hashmap_first(control->streams)))
9037 - pas_stream_set_volume_control(stream, NULL);
9038 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_UNLINK], control);
9040 while ((device = pa_hashmap_first(control->default_for_devices)))
9041 pa_device_set_default_volume_control(device, NULL);
9042 @@ -153,19 +155,10 @@ void pa_volume_control_unlink(pa_volume_control *control) {
9043 void pa_volume_control_free(pa_volume_control *control) {
9046 - if (!control->unlinked)
9047 + /* unlink() expects name to be set. */
9048 + if (!control->unlinked && control->name)
9049 pa_volume_control_unlink(control);
9051 - if (control->audio_groups) {
9052 - pa_assert(pa_hashmap_isempty(control->audio_groups));
9053 - pa_hashmap_free(control->audio_groups);
9056 - if (control->streams) {
9057 - pa_assert(pa_hashmap_isempty(control->streams));
9058 - pa_hashmap_free(control->streams);
9061 if (control->default_for_devices) {
9062 pa_assert(pa_hashmap_isempty(control->default_for_devices));
9063 pa_hashmap_free(control->default_for_devices);
9064 @@ -187,17 +180,91 @@ void pa_volume_control_free(pa_volume_control *control) {
9068 -void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group) {
9069 +void pa_volume_control_set_purpose(pa_volume_control *control, pa_volume_control_purpose_t purpose, void *owner) {
9070 + pa_assert(control);
9071 + pa_assert(!control->linked);
9073 + control->purpose = purpose;
9074 + control->owner = owner;
9077 +int pa_volume_control_acquire_for_audio_group(pa_volume_control *control, pa_audio_group *group,
9078 + pa_volume_control_set_volume_cb_t set_volume_cb, void *userdata) {
9081 + pa_assert(set_volume_cb);
9083 + if (control->present) {
9084 + pa_log("Can't acquire volume control %s, it's already present.", control->name);
9085 + return -PA_ERR_BUSY;
9088 + control->set_volume = set_volume_cb;
9089 + control->userdata = userdata;
9091 + control->set_volume_in_progress = true;
9092 + control->set_volume(control, &control->volume, &control->volume, true, true);
9093 + control->set_volume_in_progress = false;
9095 + control->present = true;
9097 + if (!control->linked || control->unlinked)
9100 + pa_log_debug("Volume control %s became present.", control->name);
9105 +void pa_volume_control_release(pa_volume_control *control) {
9106 + pa_assert(control);
9108 + if (!control->present)
9111 + control->present = false;
9113 + control->userdata = NULL;
9114 + control->set_volume = NULL;
9116 + if (!control->linked || control->unlinked)
9119 + pa_log_debug("Volume control %s became not present.", control->name);
9122 +void pa_volume_control_set_description(pa_volume_control *control, const char *description) {
9123 + char *old_description;
9125 + pa_assert(control);
9126 + pa_assert(description);
9128 - control->owner_audio_group = group;
9129 + old_description = control->description;
9131 + if (pa_streq(description, old_description))
9134 + control->description = pa_xstrdup(description);
9136 + if (control->persistent)
9137 + pa_inidb_cell_set_value(control->db_cells.description, description);
9139 + if (!control->linked || control->unlinked) {
9140 + pa_xfree(old_description);
9144 + pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
9146 + pa_xfree(old_description);
9147 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
9150 static void set_volume_internal(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance) {
9151 pa_bvolume old_volume;
9152 bool volume_changed;
9153 bool balance_changed;
9158 @@ -209,12 +276,26 @@ static void set_volume_internal(pa_volume_control *control, const pa_bvolume *vo
9159 if (!volume_changed && !balance_changed)
9162 - if (volume_changed)
9163 + if (volume_changed) {
9164 control->volume.volume = volume->volume;
9166 - if (balance_changed)
9167 + if (control->persistent) {
9168 + str = pa_sprintf_malloc("%u", control->volume.volume);
9169 + pa_inidb_cell_set_value(control->db_cells.volume, str);
9174 + if (balance_changed) {
9175 pa_bvolume_copy_balance(&control->volume, volume);
9177 + if (control->persistent) {
9178 + pa_assert_se(pa_bvolume_balance_to_string(&control->volume, &str) >= 0);
9179 + pa_inidb_cell_set_value(control->db_cells.balance, str);
9184 if (!control->linked || control->unlinked)
9187 @@ -248,62 +329,69 @@ int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *v
9191 - volume_local = *volume;
9192 + if (control->set_volume_in_progress)
9195 - if (!control->set_volume) {
9196 - pa_log_info("Tried to set the volume of volume control %s, but the volume control doesn't support the operation.",
9198 - return -PA_ERR_NOTSUPPORTED;
9200 + volume_local = *volume;
9203 - && !control->channel_map_is_writable
9204 - && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
9205 + if (set_balance && !pa_channel_map_equal(&volume_local.channel_map, &control->volume.channel_map))
9206 pa_bvolume_remap(&volume_local, &control->volume.channel_map);
9208 if (pa_bvolume_equal(&volume_local, &control->volume, set_volume, set_balance))
9211 - control->set_volume_in_progress = true;
9212 - r = control->set_volume(control, &volume_local, set_volume, set_balance);
9213 - control->set_volume_in_progress = false;
9214 + if (control->linked && control->present) {
9215 + control->set_volume_in_progress = true;
9216 + r = control->set_volume(control, volume, &volume_local, set_volume, set_balance);
9217 + control->set_volume_in_progress = false;
9220 - set_volume_internal(control, &volume_local, set_volume, set_balance);
9222 + pa_log("Setting the volume of volume control %s failed.", control->name);
9228 + set_volume_internal(control, &volume_local, set_volume, set_balance);
9233 -void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description) {
9234 - char *old_description;
9235 +void pa_volume_control_set_channel_map(pa_volume_control *control, const pa_channel_map *map) {
9236 + pa_bvolume bvolume;
9239 - pa_assert(new_description);
9242 - old_description = control->description;
9244 - if (pa_streq(new_description, old_description))
9245 + if (pa_channel_map_equal(map, &control->volume.channel_map))
9248 - control->description = pa_xstrdup(new_description);
9249 - pa_log_debug("The description of volume control %s changed from \"%s\" to \"%s\".", control->name, old_description,
9251 - pa_xfree(old_description);
9252 - pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_DESCRIPTION_CHANGED], control);
9253 + pa_bvolume_copy_balance(&bvolume, &control->volume);
9254 + pa_bvolume_remap(&bvolume, map);
9256 + set_volume_internal(control, &bvolume, false, true);
9259 -void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
9260 - bool balance_changed) {
9261 +void pa_volume_control_set_convertible_to_dB(pa_volume_control *control, bool convertible) {
9262 + bool old_convertible;
9265 - pa_assert(new_volume);
9267 - if (!control->linked)
9268 + old_convertible = control->convertible_to_dB;
9270 + if (convertible == old_convertible)
9273 - if (control->set_volume_in_progress)
9274 + control->convertible_to_dB = convertible;
9276 + if (control->persistent)
9277 + pa_inidb_cell_set_value(control->db_cells.convertible_to_dB, pa_boolean_to_string(convertible));
9279 + if (!control->linked || control->unlinked)
9282 - set_volume_internal(control, new_volume, volume_changed, balance_changed);
9283 + pa_log_debug("The volume of volume control %s became %sconvertible to dB.", control->name, convertible ? "" : "not ");
9285 + pa_hook_fire(&control->volume_api->hooks[PA_VOLUME_API_HOOK_VOLUME_CONTROL_CONVERTIBLE_TO_DB_CHANGED], control);
9288 void pa_volume_control_add_device(pa_volume_control *control, pa_device *device) {
9289 @@ -333,31 +421,3 @@ void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_
9291 pa_assert_se(pa_hashmap_remove(control->default_for_devices, device));
9294 -void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream) {
9295 - pa_assert(control);
9296 - pa_assert(stream);
9298 - pa_assert_se(pa_hashmap_put(control->streams, stream, stream) >= 0);
9301 -void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream) {
9302 - pa_assert(control);
9303 - pa_assert(stream);
9305 - pa_assert_se(pa_hashmap_remove(control->streams, stream));
9308 -void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group) {
9309 - pa_assert(control);
9312 - pa_assert_se(pa_hashmap_put(control->audio_groups, group, group) >= 0);
9315 -void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group) {
9316 - pa_assert(control);
9319 - pa_assert_se(pa_hashmap_remove(control->audio_groups, group));
9321 diff --git a/src/modules/volume-api/volume-control.h b/src/modules/volume-api/volume-control.h
9322 index aaba758..a47ab20 100644
9323 --- a/src/modules/volume-api/volume-control.h
9324 +++ b/src/modules/volume-api/volume-control.h
9328 #include <modules/volume-api/bvolume.h>
9329 +#include <modules/volume-api/inidb.h>
9330 #include <modules/volume-api/volume-api.h>
9332 typedef struct pa_volume_control pa_volume_control;
9335 + PA_VOLUME_CONTROL_PURPOSE_STREAM_RELATIVE_VOLUME,
9336 + PA_VOLUME_CONTROL_PURPOSE_OTHER,
9337 +} pa_volume_control_purpose_t;
9339 +/* Usually remapped_volume is the volume to use, because it has a matching
9340 + * channel map with the control, but in case the volume needs to be propagated
9341 + * to another control, original_volume can be used to avoid loss of precision
9342 + * that can result from remapping. */
9343 +typedef int (*pa_volume_control_set_volume_cb_t)(pa_volume_control *control, const pa_bvolume *original_volume,
9344 + const pa_bvolume *remapped_volume, bool set_volume, bool set_balance);
9346 struct pa_volume_control {
9347 pa_volume_api *volume_api;
9349 @@ -35,65 +48,61 @@ struct pa_volume_control {
9350 pa_proplist *proplist;
9352 bool convertible_to_dB;
9353 - bool channel_map_is_writable;
9357 - /* If this volume control is the "own volume control" of an audio group,
9358 - * this is set to point to that group, otherwise this is NULL. */
9359 - pa_audio_group *owner_audio_group;
9360 + pa_volume_control_purpose_t purpose;
9362 + pas_stream *owner_stream;
9366 pa_hashmap *devices; /* pa_device -> pa_device (hashmap-as-a-set) */
9367 pa_hashmap *default_for_devices; /* pa_device -> pa_device (hashmap-as-a-set) */
9368 - pa_hashmap *streams; /* pas_stream -> pas_stream (hashmap-as-a-set) */
9369 - pa_hashmap *audio_groups; /* pa_audio_group -> pa_audio_group (hashmap-as-a-set) */
9372 + pa_inidb_cell *description;
9373 + pa_inidb_cell *volume;
9374 + pa_inidb_cell *balance;
9375 + pa_inidb_cell *convertible_to_dB;
9380 bool set_volume_in_progress;
9382 /* Called from pa_volume_control_set_volume(). The implementation is
9383 - * expected to return a negative error code on failure. May be NULL, if the
9384 - * volume control is read-only. */
9385 - int (*set_volume)(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
9386 + * expected to return a negative error code on failure. */
9387 + pa_volume_control_set_volume_cb_t set_volume;
9392 -pa_volume_control *pa_volume_control_new(pa_volume_api *api, const char *name, const char *description, bool convertible_to_dB,
9393 - bool channel_map_is_writable);
9395 -typedef void (*pa_volume_control_set_initial_volume_cb_t)(pa_volume_control *control);
9397 -/* initial_volume is the preferred initial volume of the volume control
9398 - * implementation. It may be NULL or partially invalid, if the implementation
9399 - * doesn't care about the initial state of the volume control, as long as these
9400 - * two rules are followed:
9402 - * 1) Read-only volume controls must always specify fully valid initial
9404 - * 2) Volume controls with read-only channel map must always specify a valid
9405 - * channel map in initial_volume.
9407 - * The implementation's initial volume preference may be overridden by policy,
9408 - * if the volume control isn't read-only. When the final initial volume is
9409 - * known, the implementation is notified via set_initial_volume_cb (the volume
9410 - * can be read from control->volume). set_initial_volume_cb may be NULL, if the
9411 - * volume control is read-only. */
9412 -void pa_volume_control_put(pa_volume_control *control, const pa_bvolume *initial_volume,
9413 - pa_volume_control_set_initial_volume_cb_t set_initial_volume_cb);
9415 +int pa_volume_control_new(pa_volume_api *api, const char *name, bool persistent, pa_volume_control **_r);
9416 +void pa_volume_control_put(pa_volume_control *control);
9417 void pa_volume_control_unlink(pa_volume_control *control);
9418 void pa_volume_control_free(pa_volume_control *control);
9420 -/* Called by audio-group.c only. */
9421 -void pa_volume_control_set_owner_audio_group(pa_volume_control *control, pa_audio_group *group);
9422 +/* Called by the volume control implementation, before
9423 + * pa_volume_control_put(). */
9424 +void pa_volume_control_set_purpose(pa_volume_control *control, pa_volume_control_purpose_t purpose, void *owner);
9426 -/* Called by clients and policy modules. */
9427 +/* Called by the volume control implementation. */
9428 +int pa_volume_control_acquire_for_audio_group(pa_volume_control *control, pa_audio_group *group,
9429 + pa_volume_control_set_volume_cb_t set_volume_cb, void *userdata);
9431 +/* Called by the volume control implementation. This must only be called for
9432 + * persistent controls; use pa_volume_control_free() for non-persistent
9434 +void pa_volume_control_release(pa_volume_control *control);
9436 +/* Called by anyone. */
9437 +void pa_volume_control_set_description(pa_volume_control *control, const char *description);
9438 int pa_volume_control_set_volume(pa_volume_control *control, const pa_bvolume *volume, bool set_volume, bool set_balance);
9440 /* Called by the volume control implementation. */
9441 -void pa_volume_control_description_changed(pa_volume_control *control, const char *new_description);
9442 -void pa_volume_control_volume_changed(pa_volume_control *control, const pa_bvolume *new_volume, bool volume_changed,
9443 - bool balance_changed);
9444 +void pa_volume_control_set_channel_map(pa_volume_control *control, const pa_channel_map *map);
9445 +void pa_volume_control_set_convertible_to_dB(pa_volume_control *control, bool convertible);
9447 /* Called from device.c only. */
9448 void pa_volume_control_add_device(pa_volume_control *control, pa_device *device);
9449 @@ -101,12 +110,4 @@ void pa_volume_control_remove_device(pa_volume_control *control, pa_device *devi
9450 void pa_volume_control_add_default_for_device(pa_volume_control *control, pa_device *device);
9451 void pa_volume_control_remove_default_for_device(pa_volume_control *control, pa_device *device);
9453 -/* Called from sstream.c only. */
9454 -void pa_volume_control_add_stream(pa_volume_control *control, pas_stream *stream);
9455 -void pa_volume_control_remove_stream(pa_volume_control *control, pas_stream *stream);
9457 -/* Called from audio-group.c only. */
9458 -void pa_volume_control_add_audio_group(pa_volume_control *control, pa_audio_group *group);
9459 -void pa_volume_control_remove_audio_group(pa_volume_control *control, pa_audio_group *group);
9462 diff --git a/src/pulse/ext-volume-api.c b/src/pulse/ext-volume-api.c
9463 index 032e108..b81909a 100644
9464 --- a/src/pulse/ext-volume-api.c
9465 +++ b/src/pulse/ext-volume-api.c
9467 #include <pulsecore/i18n.h>
9468 #include <pulsecore/macro.h>
9469 #include <pulsecore/pstream-util.h>
9470 +#include <pulsecore/strbuf.h>
9474 @@ -94,6 +95,21 @@ void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume) {
9475 pa_channel_map_init(&volume->channel_map);
9478 +void pa_ext_volume_api_bvolume_init(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume, pa_channel_map *map) {
9481 + pa_assert(bvolume);
9482 + pa_assert(PA_VOLUME_IS_VALID(volume));
9484 + pa_assert(pa_channel_map_valid(map));
9486 + bvolume->volume = volume;
9487 + bvolume->channel_map = *map;
9489 + for (i = 0; i < map->channels; i++)
9490 + bvolume->balance[i] = 1.0;
9493 void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume) {
9495 pa_assert(PA_VOLUME_IS_VALID(volume));
9496 @@ -103,6 +119,67 @@ void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_
9497 pa_channel_map_init_mono(&bvolume->channel_map);
9500 +int pa_ext_volume_api_bvolume_parse_balance(const char *str, pa_ext_volume_api_bvolume *_r) {
9501 + pa_ext_volume_api_bvolume bvolume;
9506 + bvolume.channel_map.channels = 0;
9509 + const char *colon;
9510 + size_t channel_name_len;
9511 + char *channel_name;
9512 + pa_channel_position_t position;
9513 + const char *space;
9514 + size_t balance_str_len;
9515 + char *balance_str;
9519 + colon = strchr(str, ':');
9521 + return -PA_ERR_INVALID;
9523 + channel_name_len = colon - str;
9524 + channel_name = pa_xstrndup(str, channel_name_len);
9526 + position = pa_channel_position_from_string(channel_name);
9527 + pa_xfree(channel_name);
9528 + if (position == PA_CHANNEL_POSITION_INVALID)
9529 + return -PA_ERR_INVALID;
9531 + bvolume.channel_map.map[bvolume.channel_map.channels] = position;
9534 + space = strchr(str, ' ');
9536 + balance_str_len = space - str;
9538 + balance_str_len = strlen(str);
9540 + balance_str = pa_xstrndup(str, balance_str_len);
9542 + r = pa_atod(balance_str, &balance);
9544 + return -PA_ERR_INVALID;
9546 + if (!pa_ext_volume_api_balance_valid(balance))
9547 + return -PA_ERR_INVALID;
9549 + bvolume.balance[bvolume.channel_map.channels++] = balance;
9557 + pa_ext_volume_api_bvolume_copy_balance(_r, &bvolume);
9561 int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
9562 int check_volume, int check_balance) {
9564 @@ -266,6 +343,29 @@ void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume
9565 volume->volume = old_volume;
9568 +int pa_ext_volume_api_bvolume_balance_to_string(const pa_ext_volume_api_bvolume *volume, char **_r) {
9572 + pa_assert(volume);
9575 + if (!pa_ext_volume_api_bvolume_valid(volume, false, true))
9576 + return -PA_ERR_INVALID;
9578 + buf = pa_strbuf_new();
9580 + for (i = 0; i < volume->channel_map.channels; i++) {
9582 + pa_strbuf_putc(buf, ' ');
9584 + pa_strbuf_printf(buf, "%s:%.2f", pa_channel_position_to_string(volume->channel_map.map[i]), volume->balance[i]);
9587 + *_r = pa_strbuf_tostring_free(buf);
9591 char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_len,
9592 const pa_ext_volume_api_bvolume *volume) {
9594 diff --git a/src/pulse/ext-volume-api.h b/src/pulse/ext-volume-api.h
9595 index 720ff39..6402f4b 100644
9596 --- a/src/pulse/ext-volume-api.h
9597 +++ b/src/pulse/ext-volume-api.h
9598 @@ -50,7 +50,9 @@ int pa_ext_volume_api_balance_valid(double balance) PA_GCC_CONST;
9599 int pa_ext_volume_api_bvolume_valid(const pa_ext_volume_api_bvolume *volume, int check_volume, int check_balance)
9601 void pa_ext_volume_api_bvolume_init_invalid(pa_ext_volume_api_bvolume *volume);
9602 +void pa_ext_volume_api_bvolume_init(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume, pa_channel_map *map);
9603 void pa_ext_volume_api_bvolume_init_mono(pa_ext_volume_api_bvolume *bvolume, pa_volume_t volume);
9604 +int pa_ext_volume_api_bvolume_parse_balance(const char *str, pa_ext_volume_api_bvolume *bvolume);
9605 int pa_ext_volume_api_bvolume_equal(const pa_ext_volume_api_bvolume *a, const pa_ext_volume_api_bvolume *b,
9606 int check_volume, int check_balance) PA_GCC_PURE;
9607 void pa_ext_volume_api_bvolume_from_cvolume(pa_ext_volume_api_bvolume *bvolume, const pa_cvolume *cvolume,
9608 @@ -64,6 +66,7 @@ double pa_ext_volume_api_bvolume_get_left_right_balance(const pa_ext_volume_api_
9609 void pa_ext_volume_api_bvolume_set_left_right_balance(pa_ext_volume_api_bvolume *volume, double balance);
9610 double pa_ext_volume_api_bvolume_get_rear_front_balance(const pa_ext_volume_api_bvolume *volume) PA_GCC_PURE;
9611 void pa_ext_volume_api_bvolume_set_rear_front_balance(pa_ext_volume_api_bvolume *volume, double balance);
9612 +int pa_ext_volume_api_bvolume_balance_to_string(const pa_ext_volume_api_bvolume *volume, char **_r);
9614 #define PA_EXT_VOLUME_API_BVOLUME_SNPRINT_BALANCE_MAX 500
9615 char *pa_ext_volume_api_bvolume_snprint_balance(char *buf, size_t buf_size,
9616 diff --git a/src/tizen-ivi/audio-groups.conf b/src/tizen-ivi/audio-groups.conf
9617 index 4839307..182df0d 100644
9618 --- a/src/tizen-ivi/audio-groups.conf
9619 +++ b/src/tizen-ivi/audio-groups.conf
9622 -audio-groups = x-tizen-ivi-call-downlink-audio-group x-tizen-ivi-navigator-output-audio-group x-tizen-ivi-default-output-audio-group
9623 -streams = call-downlink navigator-output default-output
9624 +stream-rules = call-downlink navigator-output default-output
9626 [AudioGroup x-tizen-ivi-call-downlink-audio-group]
9627 description = Call downlink
9628 -volume-control = create
9629 -mute-control = create
9630 +volume-control = create:call-downlink-volume-control
9631 +mute-control = create:call-downlink-mute-control
9633 [AudioGroup x-tizen-ivi-navigator-output-audio-group]
9634 description = Navigator
9635 -volume-control = create
9636 -mute-control = create
9637 +volume-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
9638 +mute-control = bind:AudioGroup:x-tizen-ivi-default-output-audio-group
9640 [AudioGroup x-tizen-ivi-default-output-audio-group]
9641 description = Default
9642 -volume-control = create
9643 -mute-control = create
9644 +volume-control = create:default-output-volume-control
9645 +mute-control = create:default-output-mute-control
9647 -[Stream call-downlink]
9648 +[StreamRule call-downlink]
9649 match = (direction output AND property media.role=phone)
9650 audio-group-for-volume = x-tizen-ivi-call-downlink-audio-group
9651 audio-group-for-mute = x-tizen-ivi-call-downlink-audio-group
9653 -[Stream navigator-output]
9654 +[StreamRule navigator-output]
9655 match = (direction output AND property media.role=navigator)
9656 audio-group-for-volume = x-tizen-ivi-navigator-output-audio-group
9657 audio-group-for-mute = x-tizen-ivi-navigator-output-audio-group
9659 -[Stream default-output]
9660 -match = (direction output)
9661 +[StreamRule default-output]
9662 +match = (direction output AND NEG property media.role=filter)
9663 audio-group-for-volume = x-tizen-ivi-default-output-audio-group
9664 audio-group-for-mute = x-tizen-ivi-default-output-audio-group
9665 diff --git a/src/tizen-ivi/main-volume-policy.conf b/src/tizen-ivi/main-volume-policy.conf
9666 index 0a83968..f2b3513 100644
9667 --- a/src/tizen-ivi/main-volume-policy.conf
9668 +++ b/src/tizen-ivi/main-volume-policy.conf
9669 @@ -3,7 +3,6 @@ output-volume-model = by-active-main-volume-context
9670 input-volume-model = none
9671 output-mute-model = by-active-main-volume-context
9672 input-mute-model = none
9673 -main-volume-contexts = x-tizen-ivi-call default
9675 [MainVolumeContext x-tizen-ivi-call]
9676 description = Call main volume context