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
) {
137 if (u
->save_time_event
)
140 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
143 static struct entry
* entry_new(pa_bool_t add_pcm_format
) {
144 struct entry
*r
= pa_xnew0(struct entry
, 1);
145 r
->version
= ENTRY_VERSION
;
146 r
->formats
= pa_idxset_new(NULL
, NULL
);
147 if (add_pcm_format
) {
148 pa_format_info
*f
= pa_format_info_new();
149 f
->encoding
= PA_ENCODING_PCM
;
150 pa_idxset_put(r
->formats
, f
, NULL
);
155 static void entry_free(struct entry
* e
) {
158 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
163 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
175 n_formats
= pa_idxset_size(e
->formats
);
176 pa_assert(n_formats
> 0);
178 t
= pa_tagstruct_new(NULL
, 0);
179 pa_tagstruct_putu8(t
, e
->version
);
180 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
181 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
182 pa_tagstruct_put_cvolume(t
, &e
->volume
);
183 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
184 pa_tagstruct_put_boolean(t
, e
->muted
);
185 pa_tagstruct_put_boolean(t
, e
->port_valid
);
186 pa_tagstruct_puts(t
, e
->port
);
187 pa_tagstruct_putu8(t
, n_formats
);
189 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
190 pa_tagstruct_put_format_info(t
, f
);
193 key
.data
= (char *) name
;
194 key
.size
= strlen(name
);
196 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
198 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
200 pa_tagstruct_free(t
);
205 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
207 #define LEGACY_ENTRY_VERSION 2
208 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
209 struct legacy_entry
{
211 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
213 pa_channel_map channel_map
;
215 char port
[PA_NAME_MAX
];
217 struct legacy_entry
*le
;
223 if (data
->size
!= sizeof(struct legacy_entry
)) {
224 pa_log_debug("Size does not match.");
228 le
= (struct legacy_entry
*)data
->data
;
230 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
231 pa_log_debug("Version mismatch.");
235 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
236 pa_log_warn("Port has missing NUL byte.");
240 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
241 pa_log_warn("Invalid channel map.");
245 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
246 pa_log_warn("Volume and channel map don't match.");
251 e
->muted_valid
= le
->muted_valid
;
252 e
->volume_valid
= le
->volume_valid
;
253 e
->port_valid
= le
->port_valid
;
254 e
->muted
= le
->muted
;
255 e
->channel_map
= le
->channel_map
;
256 e
->volume
= le
->volume
;
257 e
->port
= pa_xstrdup(le
->port
);
262 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
264 struct entry
*e
= NULL
;
265 pa_tagstruct
*t
= NULL
;
267 uint8_t i
, n_formats
;
272 key
.data
= (char*) name
;
273 key
.size
= strlen(name
);
277 if (!pa_database_get(u
->database
, &key
, &data
))
280 t
= pa_tagstruct_new(data
.data
, data
.size
);
281 e
= entry_new(FALSE
);
283 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
284 e
->version
> ENTRY_VERSION
||
285 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
286 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
287 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
288 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
289 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
290 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
291 pa_tagstruct_gets(t
, &port
) < 0 ||
292 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
297 e
->port
= pa_xstrdup(port
);
299 for (i
= 0; i
< n_formats
; ++i
) {
300 pa_format_info
*f
= pa_format_info_new();
301 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
302 pa_format_info_free(f
);
305 pa_idxset_put(e
->formats
, f
, NULL
);
308 if (!pa_tagstruct_eof(t
))
311 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
312 pa_log_warn("Invalid channel map stored in database for device %s", name
);
316 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
317 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
321 pa_tagstruct_free(t
);
322 pa_datum_free(&data
);
328 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
333 pa_tagstruct_free(t
);
335 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
336 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
337 if ((e
= legacy_entry_read(u
, &data
))) {
338 pa_log_debug("Success. Saving new format for key: %s", name
);
339 if (entry_write(u
, name
, e
))
341 pa_datum_free(&data
);
344 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
347 pa_datum_free(&data
);
351 static struct entry
* entry_copy(const struct entry
*e
) {
357 r
= entry_new(FALSE
);
358 r
->version
= e
->version
;
359 r
->muted_valid
= e
->muted_valid
;
360 r
->volume_valid
= e
->volume_valid
;
361 r
->port_valid
= e
->port_valid
;
363 r
->channel_map
= e
->channel_map
;
364 r
->volume
= e
->volume
;
365 r
->port
= pa_xstrdup(e
->port
);
367 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
368 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
373 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
376 if (a
->port_valid
!= b
->port_valid
||
377 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
380 if (a
->muted_valid
!= b
->muted_valid
||
381 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
385 if (a
->volume_valid
!= b
->volume_valid
||
386 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
389 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
392 /** TODO: Compare a bit better */
397 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
398 struct userdata
*u
= userdata
;
399 struct entry
*entry
, *old
;
405 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
406 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
407 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
408 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
411 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
414 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
417 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
419 if ((old
= entry_read(u
, name
)))
420 entry
= entry_copy(old
);
422 entry
= entry_new(TRUE
);
424 if (sink
->save_volume
) {
425 entry
->channel_map
= sink
->channel_map
;
426 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
427 entry
->volume_valid
= TRUE
;
430 if (sink
->save_muted
) {
431 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
432 entry
->muted_valid
= TRUE
;
435 if (sink
->save_port
) {
436 pa_xfree(entry
->port
);
437 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
438 entry
->port_valid
= TRUE
;
444 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
446 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
449 name
= pa_sprintf_malloc("source:%s", source
->name
);
451 if ((old
= entry_read(u
, name
)))
452 entry
= entry_copy(old
);
454 entry
= entry_new(TRUE
);
456 if (source
->save_volume
) {
457 entry
->channel_map
= source
->channel_map
;
458 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
459 entry
->volume_valid
= TRUE
;
462 if (source
->save_muted
) {
463 entry
->muted
= pa_source_get_mute(source
, FALSE
);
464 entry
->muted_valid
= TRUE
;
467 if (source
->save_port
) {
468 pa_xfree(entry
->port
);
469 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
470 entry
->port_valid
= TRUE
;
478 if (entries_equal(old
, entry
)) {
488 pa_log_info("Storing volume/mute/port for device %s.", name
);
490 if (entry_write(u
, name
, entry
))
497 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
504 pa_assert(u
->restore_port
);
506 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
508 if ((e
= entry_read(u
, name
))) {
511 if (!new_data
->active_port
) {
512 pa_log_info("Restoring port for sink %s.", name
);
513 pa_sink_new_data_set_port(new_data
, e
->port
);
514 new_data
->save_port
= TRUE
;
516 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
527 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
534 pa_assert(u
->restore_volume
|| u
->restore_muted
);
536 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
538 if ((e
= entry_read(u
, name
))) {
540 if (u
->restore_volume
&& e
->volume_valid
) {
542 if (!new_data
->volume_is_set
) {
545 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
548 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
549 pa_sink_new_data_set_volume(new_data
, &v
);
551 new_data
->save_volume
= TRUE
;
553 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
556 if (u
->restore_muted
&& e
->muted_valid
) {
558 if (!new_data
->muted_is_set
) {
559 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
560 pa_sink_new_data_set_muted(new_data
, e
->muted
);
561 new_data
->save_muted
= TRUE
;
563 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
574 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
581 pa_assert(u
->restore_port
);
583 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
585 if ((e
= entry_read(u
, name
))) {
588 if (!new_data
->active_port
) {
589 pa_log_info("Restoring port for source %s.", name
);
590 pa_source_new_data_set_port(new_data
, e
->port
);
591 new_data
->save_port
= TRUE
;
593 pa_log_debug("Not restoring port for source %s, because already set.", name
);
604 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
611 pa_assert(u
->restore_volume
|| u
->restore_muted
);
613 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
615 if ((e
= entry_read(u
, name
))) {
617 if (u
->restore_volume
&& e
->volume_valid
) {
619 if (!new_data
->volume_is_set
) {
622 pa_log_info("Restoring volume for source %s.", new_data
->name
);
625 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
626 pa_source_new_data_set_volume(new_data
, &v
);
628 new_data
->save_volume
= TRUE
;
630 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
633 if (u
->restore_muted
&& e
->muted_valid
) {
635 if (!new_data
->muted_is_set
) {
636 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
637 pa_source_new_data_set_muted(new_data
, e
->muted
);
638 new_data
->save_muted
= TRUE
;
640 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
651 #define EXT_VERSION 1
653 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
661 pa_tagstruct_putu32(reply
, sink
->index
);
663 /* Read or create an entry */
664 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
665 if (!(e
= entry_read(u
, name
))) {
666 /* Fake a reply with PCM encoding supported */
667 pa_format_info
*f
= pa_format_info_new();
669 pa_tagstruct_putu8(reply
, 1);
670 f
->encoding
= PA_ENCODING_PCM
;
671 pa_tagstruct_put_format_info(reply
, f
);
673 pa_format_info_free(f
);
678 /* Write all the formats from the entry to the reply */
679 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
680 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
681 pa_tagstruct_put_format_info(reply
, f
);
687 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
690 pa_tagstruct
*reply
= NULL
;
699 if (pa_tagstruct_getu32(t
, &command
) < 0)
702 reply
= pa_tagstruct_new(NULL
, 0);
703 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
704 pa_tagstruct_putu32(reply
, tag
);
707 case SUBCOMMAND_TEST
: {
708 if (!pa_tagstruct_eof(t
))
711 pa_tagstruct_putu32(reply
, EXT_VERSION
);
715 case SUBCOMMAND_SUBSCRIBE
: {
719 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
720 !pa_tagstruct_eof(t
))
724 pa_idxset_put(u
->subscribed
, c
, NULL
);
726 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
731 case SUBCOMMAND_READ_SINK_FORMATS_ALL
: {
735 if (!pa_tagstruct_eof(t
))
738 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
739 read_sink_format_reply(u
, reply
, sink
);
744 case SUBCOMMAND_READ_SINK_FORMATS
: {
750 /* Get the sink index and the number of formats from the tagstruct */
751 if (pa_tagstruct_getu32(t
, &sink_index
) < 0)
754 if (!pa_tagstruct_eof(t
))
757 /* Now find our sink */
758 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
761 read_sink_format_reply(u
, reply
, sink
);
766 case SUBCOMMAND_SAVE_SINK_FORMATS
: {
772 uint8_t i
, n_formats
;
774 /* Get the sink index and the number of formats from the tagstruct */
775 if (pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
776 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
781 /* Now find our sink */
782 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
785 /* Read or create an entry */
786 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
787 if (!(e
= entry_read(u
, name
)))
788 e
= entry_new(FALSE
);
790 /* Read all the formats from our tagstruct */
791 for (i
= 0; i
< n_formats
; ++i
) {
792 pa_format_info
*f
= pa_format_info_new();
793 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
794 pa_format_info_free(f
);
798 pa_idxset_put(e
->formats
, f
, NULL
);
801 if (!pa_tagstruct_eof(t
)) {
807 if (entry_write(u
, name
, e
))
810 pa_log_warn("Could not save format info for sink %s", sink
->name
);
822 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
828 pa_tagstruct_free(reply
);
833 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
838 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
842 int pa__init(pa_module
*m
) {
843 pa_modargs
*ma
= NULL
;
849 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
;
853 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
854 pa_log("Failed to parse module arguments");
858 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
859 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
860 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0) {
861 pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments");
865 if (!restore_muted
&& !restore_volume
&& !restore_port
)
866 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
868 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
871 u
->restore_volume
= restore_volume
;
872 u
->restore_muted
= restore_muted
;
873 u
->restore_port
= restore_port
;
875 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
877 u
->protocol
= pa_native_protocol_get(m
->core
);
878 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
880 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
);
882 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
885 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
);
886 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
);
889 if (restore_muted
|| restore_volume
) {
890 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
);
891 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
);
894 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
897 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
898 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
903 pa_log_info("Successfully opened database file '%s'.", fname
);
906 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
907 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
909 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
910 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
924 void pa__done(pa_module
*m
) {
929 if (!(u
= m
->userdata
))
933 pa_subscription_free(u
->subscription
);
935 if (u
->sink_fixate_hook_slot
)
936 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
937 if (u
->source_fixate_hook_slot
)
938 pa_hook_slot_free(u
->source_fixate_hook_slot
);
939 if (u
->sink_new_hook_slot
)
940 pa_hook_slot_free(u
->sink_new_hook_slot
);
941 if (u
->source_new_hook_slot
)
942 pa_hook_slot_free(u
->source_new_hook_slot
);
944 if (u
->connection_unlink_hook_slot
)
945 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
947 if (u
->save_time_event
)
948 u
->core
->mainloop
->time_free(u
->save_time_event
);
951 pa_database_close(u
->database
);
954 pa_native_protocol_remove_ext(u
->protocol
, m
);
955 pa_native_protocol_unref(u
->protocol
);
959 pa_idxset_free(u
->subscribed
, NULL
, NULL
);