2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2011 Colin Guthrie
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
34 #include <pulse/gccmacro.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/rtclock.h>
39 #include <pulse/format.h>
40 #include <pulse/internal.h>
42 #include <pulsecore/core-error.h>
43 #include <pulsecore/module.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/modargs.h>
46 #include <pulsecore/log.h>
47 #include <pulsecore/core-subscribe.h>
48 #include <pulsecore/sink.h>
49 #include <pulsecore/source.h>
50 #include <pulsecore/namereg.h>
51 #include <pulsecore/protocol-native.h>
52 #include <pulsecore/pstream.h>
53 #include <pulsecore/pstream-util.h>
54 #include <pulsecore/database.h>
55 #include <pulsecore/tagstruct.h>
57 #include "module-device-restore-symdef.h"
59 PA_MODULE_AUTHOR("Lennart Poettering");
60 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
61 PA_MODULE_VERSION(PACKAGE_VERSION
);
62 PA_MODULE_LOAD_ONCE(TRUE
);
64 "restore_port=<Save/restore port?> "
65 "restore_volume=<Save/restore volumes?> "
66 "restore_muted=<Save/restore muted states?> "
67 "restore_formats=<Save/restore saved formats?>");
69 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
71 static const char* const valid_modargs
[] = {
82 pa_subscription
*subscription
;
85 *sink_fixate_hook_slot
,
87 *source_new_hook_slot
,
88 *source_fixate_hook_slot
,
89 *connection_unlink_hook_slot
;
90 pa_time_event
*save_time_event
;
91 pa_database
*database
;
93 pa_native_protocol
*protocol
;
94 pa_idxset
*subscribed
;
96 pa_bool_t restore_volume
:1;
97 pa_bool_t restore_muted
:1;
98 pa_bool_t restore_port
:1;
99 pa_bool_t restore_formats
:1;
102 /* Protocol extension commands */
105 SUBCOMMAND_SUBSCRIBE
,
107 SUBCOMMAND_READ_FORMATS_ALL
,
108 SUBCOMMAND_READ_FORMATS
,
109 SUBCOMMAND_SAVE_FORMATS
113 #define ENTRY_VERSION 1
117 pa_bool_t muted_valid
, volume_valid
, port_valid
;
119 pa_channel_map channel_map
;
125 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
126 struct userdata
*u
= userdata
;
132 pa_assert(e
== u
->save_time_event
);
133 u
->core
->mainloop
->time_free(u
->save_time_event
);
134 u
->save_time_event
= NULL
;
136 pa_database_sync(u
->database
);
137 pa_log_info("Synced.");
140 static void trigger_save(struct userdata
*u
, pa_device_type_t type
, uint32_t sink_idx
) {
141 pa_native_connection
*c
;
144 if (sink_idx
!= PA_INVALID_INDEX
) {
145 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
148 t
= pa_tagstruct_new(NULL
, 0);
149 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
150 pa_tagstruct_putu32(t
, 0);
151 pa_tagstruct_putu32(t
, u
->module
->index
);
152 pa_tagstruct_puts(t
, u
->module
->name
);
153 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
154 pa_tagstruct_putu32(t
, type
);
155 pa_tagstruct_putu32(t
, sink_idx
);
157 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
161 if (u
->save_time_event
)
164 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
167 static struct entry
* entry_new(pa_bool_t add_pcm_format
) {
168 struct entry
*r
= pa_xnew0(struct entry
, 1);
169 r
->version
= ENTRY_VERSION
;
170 r
->formats
= pa_idxset_new(NULL
, NULL
);
171 if (add_pcm_format
) {
172 pa_format_info
*f
= pa_format_info_new();
173 f
->encoding
= PA_ENCODING_PCM
;
174 pa_idxset_put(r
->formats
, f
, NULL
);
179 static void entry_free(struct entry
* e
) {
182 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
187 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
199 n_formats
= pa_idxset_size(e
->formats
);
200 pa_assert(n_formats
> 0);
202 t
= pa_tagstruct_new(NULL
, 0);
203 pa_tagstruct_putu8(t
, e
->version
);
204 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
205 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
206 pa_tagstruct_put_cvolume(t
, &e
->volume
);
207 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
208 pa_tagstruct_put_boolean(t
, e
->muted
);
209 pa_tagstruct_put_boolean(t
, e
->port_valid
);
210 pa_tagstruct_puts(t
, e
->port
);
211 pa_tagstruct_putu8(t
, n_formats
);
213 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
214 pa_tagstruct_put_format_info(t
, f
);
217 key
.data
= (char *) name
;
218 key
.size
= strlen(name
);
220 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
222 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
224 pa_tagstruct_free(t
);
229 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
231 #define LEGACY_ENTRY_VERSION 2
232 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
233 struct legacy_entry
{
235 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
237 pa_channel_map channel_map
;
239 char port
[PA_NAME_MAX
];
241 struct legacy_entry
*le
;
247 if (data
->size
!= sizeof(struct legacy_entry
)) {
248 pa_log_debug("Size does not match.");
252 le
= (struct legacy_entry
*)data
->data
;
254 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
255 pa_log_debug("Version mismatch.");
259 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
260 pa_log_warn("Port has missing NUL byte.");
264 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
265 pa_log_warn("Invalid channel map.");
269 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
270 pa_log_warn("Volume and channel map don't match.");
275 e
->muted_valid
= le
->muted_valid
;
276 e
->volume_valid
= le
->volume_valid
;
277 e
->port_valid
= le
->port_valid
;
278 e
->muted
= le
->muted
;
279 e
->channel_map
= le
->channel_map
;
280 e
->volume
= le
->volume
;
281 e
->port
= pa_xstrdup(le
->port
);
286 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
288 struct entry
*e
= NULL
;
289 pa_tagstruct
*t
= NULL
;
291 uint8_t i
, n_formats
;
296 key
.data
= (char*) name
;
297 key
.size
= strlen(name
);
301 if (!pa_database_get(u
->database
, &key
, &data
))
304 t
= pa_tagstruct_new(data
.data
, data
.size
);
305 e
= entry_new(FALSE
);
307 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
308 e
->version
> ENTRY_VERSION
||
309 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
310 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
311 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
312 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
313 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
314 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
315 pa_tagstruct_gets(t
, &port
) < 0 ||
316 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
321 e
->port
= pa_xstrdup(port
);
323 for (i
= 0; i
< n_formats
; ++i
) {
324 pa_format_info
*f
= pa_format_info_new();
325 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
326 pa_format_info_free(f
);
329 pa_idxset_put(e
->formats
, f
, NULL
);
332 if (!pa_tagstruct_eof(t
))
335 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
336 pa_log_warn("Invalid channel map stored in database for device %s", name
);
340 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
341 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
345 pa_tagstruct_free(t
);
346 pa_datum_free(&data
);
352 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
357 pa_tagstruct_free(t
);
359 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
360 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
361 if ((e
= legacy_entry_read(u
, &data
))) {
362 pa_log_debug("Success. Saving new format for key: %s", name
);
363 if (entry_write(u
, name
, e
))
364 trigger_save(u
, PA_DEVICE_TYPE_SINK
, PA_INVALID_INDEX
);
365 pa_datum_free(&data
);
368 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
371 pa_datum_free(&data
);
375 static struct entry
* entry_copy(const struct entry
*e
) {
381 r
= entry_new(FALSE
);
382 r
->version
= e
->version
;
383 r
->muted_valid
= e
->muted_valid
;
384 r
->volume_valid
= e
->volume_valid
;
385 r
->port_valid
= e
->port_valid
;
387 r
->channel_map
= e
->channel_map
;
388 r
->volume
= e
->volume
;
389 r
->port
= pa_xstrdup(e
->port
);
391 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
392 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
397 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
400 if (a
->port_valid
!= b
->port_valid
||
401 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
404 if (a
->muted_valid
!= b
->muted_valid
||
405 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
409 if (a
->volume_valid
!= b
->volume_valid
||
410 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
413 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
416 /** TODO: Compare a bit better */
421 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
422 struct userdata
*u
= userdata
;
423 struct entry
*entry
, *old
;
425 pa_device_type_t type
;
430 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
431 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
432 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
433 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
436 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
439 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
442 type
= PA_DEVICE_TYPE_SINK
;
443 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
445 if ((old
= entry_read(u
, name
)))
446 entry
= entry_copy(old
);
448 entry
= entry_new(TRUE
);
450 if (sink
->save_volume
) {
451 entry
->channel_map
= sink
->channel_map
;
452 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
453 entry
->volume_valid
= TRUE
;
456 if (sink
->save_muted
) {
457 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
458 entry
->muted_valid
= TRUE
;
461 if (sink
->save_port
) {
462 pa_xfree(entry
->port
);
463 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
464 entry
->port_valid
= TRUE
;
470 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
472 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
475 type
= PA_DEVICE_TYPE_SOURCE
;
476 name
= pa_sprintf_malloc("source:%s", source
->name
);
478 if ((old
= entry_read(u
, name
)))
479 entry
= entry_copy(old
);
481 entry
= entry_new(TRUE
);
483 if (source
->save_volume
) {
484 entry
->channel_map
= source
->channel_map
;
485 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
486 entry
->volume_valid
= TRUE
;
489 if (source
->save_muted
) {
490 entry
->muted
= pa_source_get_mute(source
, FALSE
);
491 entry
->muted_valid
= TRUE
;
494 if (source
->save_port
) {
495 pa_xfree(entry
->port
);
496 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
497 entry
->port_valid
= TRUE
;
505 if (entries_equal(old
, entry
)) {
515 pa_log_info("Storing volume/mute/port for device %s.", name
);
517 if (entry_write(u
, name
, entry
))
518 trigger_save(u
, type
, idx
);
524 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
531 pa_assert(u
->restore_port
);
533 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
535 if ((e
= entry_read(u
, name
))) {
538 if (!new_data
->active_port
) {
539 pa_log_info("Restoring port for sink %s.", name
);
540 pa_sink_new_data_set_port(new_data
, e
->port
);
541 new_data
->save_port
= TRUE
;
543 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
554 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
561 pa_assert(u
->restore_volume
|| u
->restore_muted
);
563 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
565 if ((e
= entry_read(u
, name
))) {
567 if (u
->restore_volume
&& e
->volume_valid
) {
569 if (!new_data
->volume_is_set
) {
572 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
575 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
576 pa_sink_new_data_set_volume(new_data
, &v
);
578 new_data
->save_volume
= TRUE
;
580 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
583 if (u
->restore_muted
&& e
->muted_valid
) {
585 if (!new_data
->muted_is_set
) {
586 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
587 pa_sink_new_data_set_muted(new_data
, e
->muted
);
588 new_data
->save_muted
= TRUE
;
590 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
601 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
608 pa_assert(u
->restore_formats
);
610 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
612 if ((e
= entry_read(u
, name
))) {
614 if (!pa_sink_set_formats(sink
, e
->formats
))
615 pa_log_debug("Could not set format on sink %s", sink
->name
);
625 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
632 pa_assert(u
->restore_port
);
634 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
636 if ((e
= entry_read(u
, name
))) {
639 if (!new_data
->active_port
) {
640 pa_log_info("Restoring port for source %s.", name
);
641 pa_source_new_data_set_port(new_data
, e
->port
);
642 new_data
->save_port
= TRUE
;
644 pa_log_debug("Not restoring port for source %s, because already set.", name
);
655 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
662 pa_assert(u
->restore_volume
|| u
->restore_muted
);
664 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
666 if ((e
= entry_read(u
, name
))) {
668 if (u
->restore_volume
&& e
->volume_valid
) {
670 if (!new_data
->volume_is_set
) {
673 pa_log_info("Restoring volume for source %s.", new_data
->name
);
676 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
677 pa_source_new_data_set_volume(new_data
, &v
);
679 new_data
->save_volume
= TRUE
;
681 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
684 if (u
->restore_muted
&& e
->muted_valid
) {
686 if (!new_data
->muted_is_set
) {
687 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
688 pa_source_new_data_set_muted(new_data
, e
->muted
);
689 new_data
->save_muted
= TRUE
;
691 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
702 #define EXT_VERSION 1
704 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
712 pa_tagstruct_putu32(reply
, PA_DEVICE_TYPE_SINK
);
713 pa_tagstruct_putu32(reply
, sink
->index
);
715 /* Read or create an entry */
716 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
717 if (!(e
= entry_read(u
, name
))) {
718 /* Fake a reply with PCM encoding supported */
719 pa_format_info
*f
= pa_format_info_new();
721 pa_tagstruct_putu8(reply
, 1);
722 f
->encoding
= PA_ENCODING_PCM
;
723 pa_tagstruct_put_format_info(reply
, f
);
725 pa_format_info_free(f
);
730 /* Write all the formats from the entry to the reply */
731 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
732 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
733 pa_tagstruct_put_format_info(reply
, f
);
739 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
742 pa_tagstruct
*reply
= NULL
;
751 if (pa_tagstruct_getu32(t
, &command
) < 0)
754 reply
= pa_tagstruct_new(NULL
, 0);
755 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
756 pa_tagstruct_putu32(reply
, tag
);
759 case SUBCOMMAND_TEST
: {
760 if (!pa_tagstruct_eof(t
))
763 pa_tagstruct_putu32(reply
, EXT_VERSION
);
767 case SUBCOMMAND_SUBSCRIBE
: {
771 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
772 !pa_tagstruct_eof(t
))
776 pa_idxset_put(u
->subscribed
, c
, NULL
);
778 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
783 case SUBCOMMAND_READ_FORMATS_ALL
: {
787 if (!pa_tagstruct_eof(t
))
790 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
791 read_sink_format_reply(u
, reply
, sink
);
796 case SUBCOMMAND_READ_FORMATS
: {
797 pa_device_type_t type
;
803 /* Get the sink index and the number of formats from the tagstruct */
804 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
805 pa_tagstruct_getu32(t
, &sink_index
) < 0)
808 if (type
!= PA_DEVICE_TYPE_SINK
) {
809 pa_log("Device format reading is only supported on sinks");
813 if (!pa_tagstruct_eof(t
))
816 /* Now find our sink */
817 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
820 read_sink_format_reply(u
, reply
, sink
);
825 case SUBCOMMAND_SAVE_FORMATS
: {
828 pa_device_type_t type
;
832 uint8_t i
, n_formats
;
834 /* Get the sink index and the number of formats from the tagstruct */
835 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
836 pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
837 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
842 if (type
!= PA_DEVICE_TYPE_SINK
) {
843 pa_log("Device format saving is only supported on sinks");
847 /* Now find our sink */
848 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
))) {
849 pa_log("Could not find sink #%d", sink_index
);
853 /* Read or create an entry */
854 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
855 if (!(e
= entry_read(u
, name
)))
856 e
= entry_new(FALSE
);
858 /* Clean out any saved formats */
859 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
860 e
->formats
= pa_idxset_new(NULL
, NULL
);
863 /* Read all the formats from our tagstruct */
864 for (i
= 0; i
< n_formats
; ++i
) {
865 pa_format_info
*f
= pa_format_info_new();
866 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
867 pa_format_info_free(f
);
871 pa_idxset_put(e
->formats
, f
, NULL
);
874 if (!pa_tagstruct_eof(t
)) {
880 if (pa_sink_set_formats(sink
, e
->formats
) && entry_write(u
, name
, e
))
881 trigger_save(u
, type
, sink_index
);
883 pa_log_warn("Could not save format info for sink %s", sink
->name
);
895 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
901 pa_tagstruct_free(reply
);
906 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
911 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
915 int pa__init(pa_module
*m
) {
916 pa_modargs
*ma
= NULL
;
922 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
, restore_formats
= TRUE
;
926 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
927 pa_log("Failed to parse module arguments");
931 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
932 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
933 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0 ||
934 pa_modargs_get_value_boolean(ma
, "restore_formats", &restore_formats
) < 0) {
935 pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
939 if (!restore_muted
&& !restore_volume
&& !restore_port
&& !restore_formats
)
940 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
942 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
945 u
->restore_volume
= restore_volume
;
946 u
->restore_muted
= restore_muted
;
947 u
->restore_port
= restore_port
;
948 u
->restore_formats
= restore_formats
;
950 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
952 u
->protocol
= pa_native_protocol_get(m
->core
);
953 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
955 u
->connection_unlink_hook_slot
= pa_hook_connect(&pa_native_protocol_hooks(u
->protocol
)[PA_NATIVE_HOOK_CONNECTION_UNLINK
], PA_HOOK_NORMAL
, (pa_hook_cb_t
) connection_unlink_hook_cb
, u
);
957 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
960 u
->sink_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_NEW
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_new_hook_callback
, u
);
961 u
->source_new_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_NEW
], PA_HOOK_EARLY
, (pa_hook_cb_t
) source_new_hook_callback
, u
);
964 if (restore_muted
|| restore_volume
) {
965 u
->sink_fixate_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_FIXATE
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_fixate_hook_callback
, u
);
966 u
->source_fixate_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_FIXATE
], PA_HOOK_EARLY
, (pa_hook_cb_t
) source_fixate_hook_callback
, u
);
970 u
->sink_put_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_PUT
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_put_hook_callback
, u
);
972 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
975 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
976 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
981 pa_log_info("Successfully opened database file '%s'.", fname
);
984 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
985 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
987 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
988 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1002 void pa__done(pa_module
*m
) {
1007 if (!(u
= m
->userdata
))
1010 if (u
->subscription
)
1011 pa_subscription_free(u
->subscription
);
1013 if (u
->sink_fixate_hook_slot
)
1014 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
1015 if (u
->source_fixate_hook_slot
)
1016 pa_hook_slot_free(u
->source_fixate_hook_slot
);
1017 if (u
->sink_new_hook_slot
)
1018 pa_hook_slot_free(u
->sink_new_hook_slot
);
1019 if (u
->source_new_hook_slot
)
1020 pa_hook_slot_free(u
->source_new_hook_slot
);
1021 if (u
->sink_put_hook_slot
)
1022 pa_hook_slot_free(u
->sink_put_hook_slot
);
1024 if (u
->connection_unlink_hook_slot
)
1025 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1027 if (u
->save_time_event
)
1028 u
->core
->mainloop
->time_free(u
->save_time_event
);
1031 pa_database_close(u
->database
);
1034 pa_native_protocol_remove_ext(u
->protocol
, m
);
1035 pa_native_protocol_unref(u
->protocol
);
1039 pa_idxset_free(u
->subscribed
, NULL
, NULL
);