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?>");
68 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
70 static const char* const valid_modargs
[] = {
80 pa_subscription
*subscription
;
83 *sink_fixate_hook_slot
,
84 *source_new_hook_slot
,
85 *source_fixate_hook_slot
,
86 *connection_unlink_hook_slot
;
87 pa_time_event
*save_time_event
;
88 pa_database
*database
;
90 pa_native_protocol
*protocol
;
91 pa_idxset
*subscribed
;
93 pa_bool_t restore_volume
:1;
94 pa_bool_t restore_muted
:1;
95 pa_bool_t restore_port
:1;
98 /* Protocol extention commands */
101 SUBCOMMAND_SUBSCRIBE
,
103 SUBCOMMAND_READ_SINK_FORMATS_ALL
,
104 SUBCOMMAND_READ_SINK_FORMATS
,
105 SUBCOMMAND_SAVE_SINK_FORMATS
109 #define ENTRY_VERSION 1
113 pa_bool_t muted_valid
, volume_valid
, port_valid
;
115 pa_channel_map channel_map
;
121 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
122 struct userdata
*u
= userdata
;
128 pa_assert(e
== u
->save_time_event
);
129 u
->core
->mainloop
->time_free(u
->save_time_event
);
130 u
->save_time_event
= NULL
;
132 pa_database_sync(u
->database
);
133 pa_log_info("Synced.");
136 static void trigger_save(struct userdata
*u
, uint32_t sink_idx
) {
137 pa_native_connection
*c
;
140 if (sink_idx
!= PA_INVALID_INDEX
) {
141 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
144 t
= pa_tagstruct_new(NULL
, 0);
145 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
146 pa_tagstruct_putu32(t
, 0);
147 pa_tagstruct_putu32(t
, u
->module
->index
);
148 pa_tagstruct_puts(t
, u
->module
->name
);
149 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
150 pa_tagstruct_putu32(t
, sink_idx
);
152 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
156 if (u
->save_time_event
)
159 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
162 static struct entry
* entry_new(pa_bool_t add_pcm_format
) {
163 struct entry
*r
= pa_xnew0(struct entry
, 1);
164 r
->version
= ENTRY_VERSION
;
165 r
->formats
= pa_idxset_new(NULL
, NULL
);
166 if (add_pcm_format
) {
167 pa_format_info
*f
= pa_format_info_new();
168 f
->encoding
= PA_ENCODING_PCM
;
169 pa_idxset_put(r
->formats
, f
, NULL
);
174 static void entry_free(struct entry
* e
) {
177 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
182 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
194 n_formats
= pa_idxset_size(e
->formats
);
195 pa_assert(n_formats
> 0);
197 t
= pa_tagstruct_new(NULL
, 0);
198 pa_tagstruct_putu8(t
, e
->version
);
199 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
200 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
201 pa_tagstruct_put_cvolume(t
, &e
->volume
);
202 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
203 pa_tagstruct_put_boolean(t
, e
->muted
);
204 pa_tagstruct_put_boolean(t
, e
->port_valid
);
205 pa_tagstruct_puts(t
, e
->port
);
206 pa_tagstruct_putu8(t
, n_formats
);
208 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
209 pa_tagstruct_put_format_info(t
, f
);
212 key
.data
= (char *) name
;
213 key
.size
= strlen(name
);
215 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
217 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
219 pa_tagstruct_free(t
);
224 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
226 #define LEGACY_ENTRY_VERSION 2
227 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
228 struct legacy_entry
{
230 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
232 pa_channel_map channel_map
;
234 char port
[PA_NAME_MAX
];
236 struct legacy_entry
*le
;
242 if (data
->size
!= sizeof(struct legacy_entry
)) {
243 pa_log_debug("Size does not match.");
247 le
= (struct legacy_entry
*)data
->data
;
249 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
250 pa_log_debug("Version mismatch.");
254 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
255 pa_log_warn("Port has missing NUL byte.");
259 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
260 pa_log_warn("Invalid channel map.");
264 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
265 pa_log_warn("Volume and channel map don't match.");
270 e
->muted_valid
= le
->muted_valid
;
271 e
->volume_valid
= le
->volume_valid
;
272 e
->port_valid
= le
->port_valid
;
273 e
->muted
= le
->muted
;
274 e
->channel_map
= le
->channel_map
;
275 e
->volume
= le
->volume
;
276 e
->port
= pa_xstrdup(le
->port
);
281 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
283 struct entry
*e
= NULL
;
284 pa_tagstruct
*t
= NULL
;
286 uint8_t i
, n_formats
;
291 key
.data
= (char*) name
;
292 key
.size
= strlen(name
);
296 if (!pa_database_get(u
->database
, &key
, &data
))
299 t
= pa_tagstruct_new(data
.data
, data
.size
);
300 e
= entry_new(FALSE
);
302 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
303 e
->version
> ENTRY_VERSION
||
304 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
305 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
306 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
307 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
308 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
309 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
310 pa_tagstruct_gets(t
, &port
) < 0 ||
311 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
316 e
->port
= pa_xstrdup(port
);
318 for (i
= 0; i
< n_formats
; ++i
) {
319 pa_format_info
*f
= pa_format_info_new();
320 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
321 pa_format_info_free(f
);
324 pa_idxset_put(e
->formats
, f
, NULL
);
327 if (!pa_tagstruct_eof(t
))
330 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
331 pa_log_warn("Invalid channel map stored in database for device %s", name
);
335 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
336 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
340 pa_tagstruct_free(t
);
341 pa_datum_free(&data
);
347 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
352 pa_tagstruct_free(t
);
354 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
355 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
356 if ((e
= legacy_entry_read(u
, &data
))) {
357 pa_log_debug("Success. Saving new format for key: %s", name
);
358 if (entry_write(u
, name
, e
))
359 trigger_save(u
, PA_INVALID_INDEX
);
360 pa_datum_free(&data
);
363 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
366 pa_datum_free(&data
);
370 static struct entry
* entry_copy(const struct entry
*e
) {
376 r
= entry_new(FALSE
);
377 r
->version
= e
->version
;
378 r
->muted_valid
= e
->muted_valid
;
379 r
->volume_valid
= e
->volume_valid
;
380 r
->port_valid
= e
->port_valid
;
382 r
->channel_map
= e
->channel_map
;
383 r
->volume
= e
->volume
;
384 r
->port
= pa_xstrdup(e
->port
);
386 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
387 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
392 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
395 if (a
->port_valid
!= b
->port_valid
||
396 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
399 if (a
->muted_valid
!= b
->muted_valid
||
400 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
404 if (a
->volume_valid
!= b
->volume_valid
||
405 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
408 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
411 /** TODO: Compare a bit better */
416 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
417 struct userdata
*u
= userdata
;
418 struct entry
*entry
, *old
;
424 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
425 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
426 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
427 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
430 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
433 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
436 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
438 if ((old
= entry_read(u
, name
)))
439 entry
= entry_copy(old
);
441 entry
= entry_new(TRUE
);
443 if (sink
->save_volume
) {
444 entry
->channel_map
= sink
->channel_map
;
445 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
446 entry
->volume_valid
= TRUE
;
449 if (sink
->save_muted
) {
450 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
451 entry
->muted_valid
= TRUE
;
454 if (sink
->save_port
) {
455 pa_xfree(entry
->port
);
456 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
457 entry
->port_valid
= TRUE
;
463 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
465 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
468 name
= pa_sprintf_malloc("source:%s", source
->name
);
470 if ((old
= entry_read(u
, name
)))
471 entry
= entry_copy(old
);
473 entry
= entry_new(TRUE
);
475 if (source
->save_volume
) {
476 entry
->channel_map
= source
->channel_map
;
477 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
478 entry
->volume_valid
= TRUE
;
481 if (source
->save_muted
) {
482 entry
->muted
= pa_source_get_mute(source
, FALSE
);
483 entry
->muted_valid
= TRUE
;
486 if (source
->save_port
) {
487 pa_xfree(entry
->port
);
488 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
489 entry
->port_valid
= TRUE
;
497 if (entries_equal(old
, entry
)) {
507 pa_log_info("Storing volume/mute/port for device %s.", name
);
509 if (entry_write(u
, name
, entry
))
510 trigger_save(u
, idx
);
516 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
523 pa_assert(u
->restore_port
);
525 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
527 if ((e
= entry_read(u
, name
))) {
530 if (!new_data
->active_port
) {
531 pa_log_info("Restoring port for sink %s.", name
);
532 pa_sink_new_data_set_port(new_data
, e
->port
);
533 new_data
->save_port
= TRUE
;
535 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
546 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
553 pa_assert(u
->restore_volume
|| u
->restore_muted
);
555 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
557 if ((e
= entry_read(u
, name
))) {
559 if (u
->restore_volume
&& e
->volume_valid
) {
561 if (!new_data
->volume_is_set
) {
564 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
567 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
568 pa_sink_new_data_set_volume(new_data
, &v
);
570 new_data
->save_volume
= TRUE
;
572 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
575 if (u
->restore_muted
&& e
->muted_valid
) {
577 if (!new_data
->muted_is_set
) {
578 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
579 pa_sink_new_data_set_muted(new_data
, e
->muted
);
580 new_data
->save_muted
= TRUE
;
582 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
593 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
600 pa_assert(u
->restore_port
);
602 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
604 if ((e
= entry_read(u
, name
))) {
607 if (!new_data
->active_port
) {
608 pa_log_info("Restoring port for source %s.", name
);
609 pa_source_new_data_set_port(new_data
, e
->port
);
610 new_data
->save_port
= TRUE
;
612 pa_log_debug("Not restoring port for source %s, because already set.", name
);
623 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
630 pa_assert(u
->restore_volume
|| u
->restore_muted
);
632 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
634 if ((e
= entry_read(u
, name
))) {
636 if (u
->restore_volume
&& e
->volume_valid
) {
638 if (!new_data
->volume_is_set
) {
641 pa_log_info("Restoring volume for source %s.", new_data
->name
);
644 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
645 pa_source_new_data_set_volume(new_data
, &v
);
647 new_data
->save_volume
= TRUE
;
649 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
652 if (u
->restore_muted
&& e
->muted_valid
) {
654 if (!new_data
->muted_is_set
) {
655 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
656 pa_source_new_data_set_muted(new_data
, e
->muted
);
657 new_data
->save_muted
= TRUE
;
659 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
670 #define EXT_VERSION 1
672 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
680 pa_tagstruct_putu32(reply
, sink
->index
);
682 /* Read or create an entry */
683 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
684 if (!(e
= entry_read(u
, name
))) {
685 /* Fake a reply with PCM encoding supported */
686 pa_format_info
*f
= pa_format_info_new();
688 pa_tagstruct_putu8(reply
, 1);
689 f
->encoding
= PA_ENCODING_PCM
;
690 pa_tagstruct_put_format_info(reply
, f
);
692 pa_format_info_free(f
);
697 /* Write all the formats from the entry to the reply */
698 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
699 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
700 pa_tagstruct_put_format_info(reply
, f
);
706 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
709 pa_tagstruct
*reply
= NULL
;
718 if (pa_tagstruct_getu32(t
, &command
) < 0)
721 reply
= pa_tagstruct_new(NULL
, 0);
722 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
723 pa_tagstruct_putu32(reply
, tag
);
726 case SUBCOMMAND_TEST
: {
727 if (!pa_tagstruct_eof(t
))
730 pa_tagstruct_putu32(reply
, EXT_VERSION
);
734 case SUBCOMMAND_SUBSCRIBE
: {
738 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
739 !pa_tagstruct_eof(t
))
743 pa_idxset_put(u
->subscribed
, c
, NULL
);
745 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
750 case SUBCOMMAND_READ_SINK_FORMATS_ALL
: {
754 if (!pa_tagstruct_eof(t
))
757 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
758 read_sink_format_reply(u
, reply
, sink
);
763 case SUBCOMMAND_READ_SINK_FORMATS
: {
769 /* Get the sink index and the number of formats from the tagstruct */
770 if (pa_tagstruct_getu32(t
, &sink_index
) < 0)
773 if (!pa_tagstruct_eof(t
))
776 /* Now find our sink */
777 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
780 read_sink_format_reply(u
, reply
, sink
);
785 case SUBCOMMAND_SAVE_SINK_FORMATS
: {
791 uint8_t i
, n_formats
;
793 /* Get the sink index and the number of formats from the tagstruct */
794 if (pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
795 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
800 /* Now find our sink */
801 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
804 /* Read or create an entry */
805 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
806 if (!(e
= entry_read(u
, name
)))
807 e
= entry_new(FALSE
);
809 /* Clean out any saved formats */
810 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
811 e
->formats
= pa_idxset_new(NULL
, NULL
);
814 /* Read all the formats from our tagstruct */
815 for (i
= 0; i
< n_formats
; ++i
) {
816 pa_format_info
*f
= pa_format_info_new();
817 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
818 pa_format_info_free(f
);
822 pa_idxset_put(e
->formats
, f
, NULL
);
825 if (!pa_tagstruct_eof(t
)) {
831 if (entry_write(u
, name
, e
))
832 trigger_save(u
, sink_index
);
834 pa_log_warn("Could not save format info for sink %s", sink
->name
);
846 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
852 pa_tagstruct_free(reply
);
857 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
862 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
866 int pa__init(pa_module
*m
) {
867 pa_modargs
*ma
= NULL
;
873 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
;
877 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
878 pa_log("Failed to parse module arguments");
882 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
883 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
884 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0) {
885 pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments");
889 if (!restore_muted
&& !restore_volume
&& !restore_port
)
890 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
892 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
895 u
->restore_volume
= restore_volume
;
896 u
->restore_muted
= restore_muted
;
897 u
->restore_port
= restore_port
;
899 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
901 u
->protocol
= pa_native_protocol_get(m
->core
);
902 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
904 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
);
906 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
909 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
);
910 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
);
913 if (restore_muted
|| restore_volume
) {
914 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
);
915 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
);
918 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
921 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
922 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
927 pa_log_info("Successfully opened database file '%s'.", fname
);
930 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
931 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
933 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
934 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
948 void pa__done(pa_module
*m
) {
953 if (!(u
= m
->userdata
))
957 pa_subscription_free(u
->subscription
);
959 if (u
->sink_fixate_hook_slot
)
960 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
961 if (u
->source_fixate_hook_slot
)
962 pa_hook_slot_free(u
->source_fixate_hook_slot
);
963 if (u
->sink_new_hook_slot
)
964 pa_hook_slot_free(u
->sink_new_hook_slot
);
965 if (u
->source_new_hook_slot
)
966 pa_hook_slot_free(u
->source_new_hook_slot
);
968 if (u
->connection_unlink_hook_slot
)
969 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
971 if (u
->save_time_event
)
972 u
->core
->mainloop
->time_free(u
->save_time_event
);
975 pa_database_close(u
->database
);
978 pa_native_protocol_remove_ext(u
->protocol
, m
);
979 pa_native_protocol_unref(u
->protocol
);
983 pa_idxset_free(u
->subscribed
, NULL
, NULL
);