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>
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
40 #include <pulse/format.h>
41 #include <pulse/internal.h>
43 #include <pulsecore/core-error.h>
44 #include <pulsecore/module.h>
45 #include <pulsecore/core-util.h>
46 #include <pulsecore/modargs.h>
47 #include <pulsecore/log.h>
48 #include <pulsecore/core-subscribe.h>
49 #include <pulsecore/sink-input.h>
50 #include <pulsecore/source-output.h>
51 #include <pulsecore/namereg.h>
52 #include <pulsecore/protocol-native.h>
53 #include <pulsecore/pstream.h>
54 #include <pulsecore/pstream-util.h>
55 #include <pulsecore/database.h>
56 #include <pulsecore/tagstruct.h>
58 #include "module-device-restore-symdef.h"
60 PA_MODULE_AUTHOR("Lennart Poettering");
61 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
62 PA_MODULE_VERSION(PACKAGE_VERSION
);
63 PA_MODULE_LOAD_ONCE(TRUE
);
65 "restore_port=<Save/restore port?> "
66 "restore_volume=<Save/restore volumes?> "
67 "restore_muted=<Save/restore muted states?>");
69 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
71 static const char* const valid_modargs
[] = {
81 pa_subscription
*subscription
;
84 *sink_fixate_hook_slot
,
85 *source_new_hook_slot
,
86 *source_fixate_hook_slot
,
87 *connection_unlink_hook_slot
;
88 pa_time_event
*save_time_event
;
89 pa_database
*database
;
91 pa_native_protocol
*protocol
;
92 pa_idxset
*subscribed
;
94 pa_bool_t restore_volume
:1;
95 pa_bool_t restore_muted
:1;
96 pa_bool_t restore_port
:1;
99 /* Protocol extention commands */
102 SUBCOMMAND_SUBSCRIBE
,
104 SUBCOMMAND_READ_SINK_FORMATS_ALL
,
105 SUBCOMMAND_READ_SINK_FORMATS
,
106 SUBCOMMAND_SAVE_SINK_FORMATS
110 #define ENTRY_VERSION 1
114 pa_bool_t muted_valid
, volume_valid
, port_valid
;
116 pa_channel_map channel_map
;
122 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
123 struct userdata
*u
= userdata
;
129 pa_assert(e
== u
->save_time_event
);
130 u
->core
->mainloop
->time_free(u
->save_time_event
);
131 u
->save_time_event
= NULL
;
133 pa_database_sync(u
->database
);
134 pa_log_info("Synced.");
137 static void trigger_save(struct userdata
*u
) {
138 if (u
->save_time_event
)
141 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
144 static struct entry
* entry_new(pa_bool_t add_pcm_format
) {
145 struct entry
*r
= pa_xnew0(struct entry
, 1);
146 r
->version
= ENTRY_VERSION
;
147 r
->formats
= pa_idxset_new(NULL
, NULL
);
148 if (add_pcm_format
) {
149 pa_format_info
*f
= pa_format_info_new();
150 f
->encoding
= PA_ENCODING_PCM
;
151 pa_idxset_put(r
->formats
, f
, NULL
);
156 static void entry_free(struct entry
* e
) {
159 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
164 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
176 n_formats
= pa_idxset_size(e
->formats
);
177 pa_assert(n_formats
> 0);
179 t
= pa_tagstruct_new(NULL
, 0);
180 pa_tagstruct_putu8(t
, e
->version
);
181 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
182 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
183 pa_tagstruct_put_cvolume(t
, &e
->volume
);
184 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
185 pa_tagstruct_put_boolean(t
, e
->muted
);
186 pa_tagstruct_put_boolean(t
, e
->port_valid
);
187 pa_tagstruct_puts(t
, e
->port
);
188 pa_tagstruct_putu8(t
, n_formats
);
190 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
191 pa_tagstruct_put_format_info(t
, f
);
194 key
.data
= (char *) name
;
195 key
.size
= strlen(name
);
197 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
199 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
201 pa_tagstruct_free(t
);
206 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
208 #define LEGACY_ENTRY_VERSION 2
209 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
210 struct legacy_entry
{
212 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
214 pa_channel_map channel_map
;
216 char port
[PA_NAME_MAX
];
218 struct legacy_entry
*le
;
224 if (data
->size
!= sizeof(struct legacy_entry
)) {
225 pa_log_debug("Size does not match.");
229 le
= (struct legacy_entry
*)data
->data
;
231 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
232 pa_log_debug("Version mismatch.");
236 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
237 pa_log_warn("Port has missing NUL byte.");
241 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
242 pa_log_warn("Invalid channel map.");
246 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
247 pa_log_warn("Volume and channel map don't match.");
252 e
->muted_valid
= le
->muted_valid
;
253 e
->volume_valid
= le
->volume_valid
;
254 e
->port_valid
= le
->port_valid
;
255 e
->muted
= le
->muted
;
256 e
->channel_map
= le
->channel_map
;
257 e
->volume
= le
->volume
;
258 e
->port
= pa_xstrdup(le
->port
);
263 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
265 struct entry
*e
= NULL
;
266 pa_tagstruct
*t
= NULL
;
268 uint8_t i
, n_formats
;
273 key
.data
= (char*) name
;
274 key
.size
= strlen(name
);
278 if (!pa_database_get(u
->database
, &key
, &data
))
281 t
= pa_tagstruct_new(data
.data
, data
.size
);
282 e
= entry_new(FALSE
);
284 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
285 e
->version
> ENTRY_VERSION
||
286 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
287 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
288 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
289 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
290 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
291 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
292 pa_tagstruct_gets(t
, &port
) < 0 ||
293 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
298 e
->port
= pa_xstrdup(port
);
300 for (i
= 0; i
< n_formats
; ++i
) {
301 pa_format_info
*f
= pa_format_info_new();
302 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
303 pa_format_info_free(f
);
306 pa_idxset_put(e
->formats
, f
, NULL
);
309 if (!pa_tagstruct_eof(t
))
312 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
313 pa_log_warn("Invalid channel map stored in database for device %s", name
);
317 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
318 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
322 pa_tagstruct_free(t
);
323 pa_datum_free(&data
);
329 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
334 pa_tagstruct_free(t
);
336 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
337 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
338 if ((e
= legacy_entry_read(u
, &data
))) {
339 pa_log_debug("Success. Saving new format for key: %s", name
);
340 if (entry_write(u
, name
, e
))
342 pa_datum_free(&data
);
345 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
348 pa_datum_free(&data
);
352 static struct entry
* entry_copy(const struct entry
*e
) {
358 r
= entry_new(FALSE
);
359 r
->version
= e
->version
;
360 r
->muted_valid
= e
->muted_valid
;
361 r
->volume_valid
= e
->volume_valid
;
362 r
->port_valid
= e
->port_valid
;
364 r
->channel_map
= e
->channel_map
;
365 r
->volume
= e
->volume
;
366 r
->port
= pa_xstrdup(e
->port
);
368 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
369 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
374 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
377 if (a
->port_valid
!= b
->port_valid
||
378 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
381 if (a
->muted_valid
!= b
->muted_valid
||
382 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
386 if (a
->volume_valid
!= b
->volume_valid
||
387 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
390 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
393 /** TODO: Compare a bit better */
398 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
399 struct userdata
*u
= userdata
;
400 struct entry
*entry
, *old
;
406 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
407 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
408 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
409 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
412 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
415 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
418 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
420 if ((old
= entry_read(u
, name
)))
421 entry
= entry_copy(old
);
423 entry
= entry_new(TRUE
);
425 if (sink
->save_volume
) {
426 entry
->channel_map
= sink
->channel_map
;
427 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
428 entry
->volume_valid
= TRUE
;
431 if (sink
->save_muted
) {
432 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
433 entry
->muted_valid
= TRUE
;
436 if (sink
->save_port
) {
437 pa_xfree(entry
->port
);
438 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
439 entry
->port_valid
= TRUE
;
445 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
447 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
450 name
= pa_sprintf_malloc("source:%s", source
->name
);
452 if ((old
= entry_read(u
, name
)))
453 entry
= entry_copy(old
);
455 entry
= entry_new(TRUE
);
457 if (source
->save_volume
) {
458 entry
->channel_map
= source
->channel_map
;
459 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
460 entry
->volume_valid
= TRUE
;
463 if (source
->save_muted
) {
464 entry
->muted
= pa_source_get_mute(source
, FALSE
);
465 entry
->muted_valid
= TRUE
;
468 if (source
->save_port
) {
469 pa_xfree(entry
->port
);
470 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
471 entry
->port_valid
= TRUE
;
479 if (entries_equal(old
, entry
)) {
489 pa_log_info("Storing volume/mute/port for device %s.", name
);
491 if (entry_write(u
, name
, entry
))
498 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
505 pa_assert(u
->restore_port
);
507 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
509 if ((e
= entry_read(u
, name
))) {
512 if (!new_data
->active_port
) {
513 pa_log_info("Restoring port for sink %s.", name
);
514 pa_sink_new_data_set_port(new_data
, e
->port
);
515 new_data
->save_port
= TRUE
;
517 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
528 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
535 pa_assert(u
->restore_volume
|| u
->restore_muted
);
537 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
539 if ((e
= entry_read(u
, name
))) {
541 if (u
->restore_volume
&& e
->volume_valid
) {
543 if (!new_data
->volume_is_set
) {
546 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
549 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
550 pa_sink_new_data_set_volume(new_data
, &v
);
552 new_data
->save_volume
= TRUE
;
554 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
557 if (u
->restore_muted
&& e
->muted_valid
) {
559 if (!new_data
->muted_is_set
) {
560 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
561 pa_sink_new_data_set_muted(new_data
, e
->muted
);
562 new_data
->save_muted
= TRUE
;
564 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
575 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
582 pa_assert(u
->restore_port
);
584 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
586 if ((e
= entry_read(u
, name
))) {
589 if (!new_data
->active_port
) {
590 pa_log_info("Restoring port for source %s.", name
);
591 pa_source_new_data_set_port(new_data
, e
->port
);
592 new_data
->save_port
= TRUE
;
594 pa_log_debug("Not restoring port for source %s, because already set.", name
);
605 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
612 pa_assert(u
->restore_volume
|| u
->restore_muted
);
614 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
616 if ((e
= entry_read(u
, name
))) {
618 if (u
->restore_volume
&& e
->volume_valid
) {
620 if (!new_data
->volume_is_set
) {
623 pa_log_info("Restoring volume for source %s.", new_data
->name
);
626 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
627 pa_source_new_data_set_volume(new_data
, &v
);
629 new_data
->save_volume
= TRUE
;
631 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
634 if (u
->restore_muted
&& e
->muted_valid
) {
636 if (!new_data
->muted_is_set
) {
637 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
638 pa_source_new_data_set_muted(new_data
, e
->muted
);
639 new_data
->save_muted
= TRUE
;
641 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
652 #define EXT_VERSION 1
654 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
662 pa_tagstruct_putu32(reply
, sink
->index
);
664 /* Read or create an entry */
665 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
666 if (!(e
= entry_read(u
, name
))) {
667 /* Fake a reply with PCM encoding supported */
668 pa_format_info
*f
= pa_format_info_new();
670 pa_tagstruct_putu8(reply
, 1);
671 f
->encoding
= PA_ENCODING_PCM
;
672 pa_tagstruct_put_format_info(reply
, f
);
674 pa_format_info_free(f
);
679 /* Write all the formats from the entry to the reply */
680 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
681 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
682 pa_tagstruct_put_format_info(reply
, f
);
688 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
691 pa_tagstruct
*reply
= NULL
;
700 if (pa_tagstruct_getu32(t
, &command
) < 0)
703 reply
= pa_tagstruct_new(NULL
, 0);
704 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
705 pa_tagstruct_putu32(reply
, tag
);
708 case SUBCOMMAND_TEST
: {
709 if (!pa_tagstruct_eof(t
))
712 pa_tagstruct_putu32(reply
, EXT_VERSION
);
716 case SUBCOMMAND_SUBSCRIBE
: {
720 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
721 !pa_tagstruct_eof(t
))
725 pa_idxset_put(u
->subscribed
, c
, NULL
);
727 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
732 case SUBCOMMAND_READ_SINK_FORMATS_ALL
: {
736 if (!pa_tagstruct_eof(t
))
739 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
740 read_sink_format_reply(u
, reply
, sink
);
745 case SUBCOMMAND_READ_SINK_FORMATS
: {
751 /* Get the sink index and the number of formats from the tagstruct */
752 if (pa_tagstruct_getu32(t
, &sink_index
) < 0)
755 if (!pa_tagstruct_eof(t
))
758 /* Now find our sink */
759 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
762 read_sink_format_reply(u
, reply
, sink
);
767 case SUBCOMMAND_SAVE_SINK_FORMATS
: {
773 uint8_t i
, n_formats
;
775 /* Get the sink index and the number of formats from the tagstruct */
776 if (pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
777 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
782 /* Now find our sink */
783 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
786 /* Read or create an entry */
787 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
788 if (!(e
= entry_read(u
, name
)))
789 e
= entry_new(FALSE
);
791 /* Read all the formats from our tagstruct */
792 for (i
= 0; i
< n_formats
; ++i
) {
793 pa_format_info
*f
= pa_format_info_new();
794 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
795 pa_format_info_free(f
);
799 pa_idxset_put(e
->formats
, f
, NULL
);
802 if (!pa_tagstruct_eof(t
)) {
808 if (entry_write(u
, name
, e
))
811 pa_log_warn("Could not save format info for sink %s", sink
->name
);
823 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
829 pa_tagstruct_free(reply
);
834 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
839 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
843 int pa__init(pa_module
*m
) {
844 pa_modargs
*ma
= NULL
;
850 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
;
854 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
855 pa_log("Failed to parse module arguments");
859 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
860 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
861 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0) {
862 pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments");
866 if (!restore_muted
&& !restore_volume
&& !restore_port
)
867 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
869 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
872 u
->restore_volume
= restore_volume
;
873 u
->restore_muted
= restore_muted
;
874 u
->restore_port
= restore_port
;
876 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
878 u
->protocol
= pa_native_protocol_get(m
->core
);
879 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
881 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
);
883 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
886 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
);
887 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
);
890 if (restore_muted
|| restore_volume
) {
891 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
);
892 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
);
895 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
898 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
899 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
904 pa_log_info("Successfully opened database file '%s'.", fname
);
907 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
908 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
910 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
911 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
925 void pa__done(pa_module
*m
) {
930 if (!(u
= m
->userdata
))
934 pa_subscription_free(u
->subscription
);
936 if (u
->sink_fixate_hook_slot
)
937 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
938 if (u
->source_fixate_hook_slot
)
939 pa_hook_slot_free(u
->source_fixate_hook_slot
);
940 if (u
->sink_new_hook_slot
)
941 pa_hook_slot_free(u
->sink_new_hook_slot
);
942 if (u
->source_new_hook_slot
)
943 pa_hook_slot_free(u
->source_new_hook_slot
);
945 if (u
->connection_unlink_hook_slot
)
946 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
948 if (u
->save_time_event
)
949 u
->core
->mainloop
->time_free(u
->save_time_event
);
952 pa_database_close(u
->database
);
955 pa_native_protocol_remove_ext(u
->protocol
, m
);
956 pa_native_protocol_unref(u
->protocol
);
960 pa_idxset_free(u
->subscribed
, NULL
, NULL
);