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
;
97 pa_bool_t restore_muted
;
98 pa_bool_t restore_port
;
99 pa_bool_t restore_formats
;
102 /* Protocol extention commands */
105 SUBCOMMAND_SUBSCRIBE
,
107 SUBCOMMAND_READ_SINK_FORMATS_ALL
,
108 SUBCOMMAND_READ_SINK_FORMATS
,
109 SUBCOMMAND_SAVE_SINK_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
, 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
, sink_idx
);
156 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
160 if (u
->save_time_event
)
163 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
166 static struct entry
* entry_new(pa_bool_t add_pcm_format
) {
167 struct entry
*r
= pa_xnew0(struct entry
, 1);
168 r
->version
= ENTRY_VERSION
;
169 r
->formats
= pa_idxset_new(NULL
, NULL
);
170 if (add_pcm_format
) {
171 pa_format_info
*f
= pa_format_info_new();
172 f
->encoding
= PA_ENCODING_PCM
;
173 pa_idxset_put(r
->formats
, f
, NULL
);
178 static void entry_free(struct entry
* e
) {
181 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
186 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
198 n_formats
= pa_idxset_size(e
->formats
);
199 pa_assert(n_formats
> 0);
201 t
= pa_tagstruct_new(NULL
, 0);
202 pa_tagstruct_putu8(t
, e
->version
);
203 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
204 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
205 pa_tagstruct_put_cvolume(t
, &e
->volume
);
206 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
207 pa_tagstruct_put_boolean(t
, e
->muted
);
208 pa_tagstruct_put_boolean(t
, e
->port_valid
);
209 pa_tagstruct_puts(t
, e
->port
);
210 pa_tagstruct_putu8(t
, n_formats
);
212 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
213 pa_tagstruct_put_format_info(t
, f
);
216 key
.data
= (char *) name
;
217 key
.size
= strlen(name
);
219 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
221 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
223 pa_tagstruct_free(t
);
228 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
230 #define LEGACY_ENTRY_VERSION 2
231 static struct entry
* legacy_entry_read(struct userdata
*u
, pa_datum
*data
) {
232 struct legacy_entry
{
234 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
236 pa_channel_map channel_map
;
238 char port
[PA_NAME_MAX
];
240 struct legacy_entry
*le
;
246 if (data
->size
!= sizeof(struct legacy_entry
)) {
247 pa_log_debug("Size does not match.");
251 le
= (struct legacy_entry
*)data
->data
;
253 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
254 pa_log_debug("Version mismatch.");
258 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
259 pa_log_warn("Port has missing NUL byte.");
263 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
264 pa_log_warn("Invalid channel map.");
268 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
269 pa_log_warn("Volume and channel map don't match.");
274 e
->muted_valid
= le
->muted_valid
;
275 e
->volume_valid
= le
->volume_valid
;
276 e
->port_valid
= le
->port_valid
;
277 e
->muted
= le
->muted
;
278 e
->channel_map
= le
->channel_map
;
279 e
->volume
= le
->volume
;
280 e
->port
= pa_xstrdup(le
->port
);
285 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
287 struct entry
*e
= NULL
;
288 pa_tagstruct
*t
= NULL
;
290 uint8_t i
, n_formats
;
295 key
.data
= (char*) name
;
296 key
.size
= strlen(name
);
300 if (!pa_database_get(u
->database
, &key
, &data
))
303 t
= pa_tagstruct_new(data
.data
, data
.size
);
304 e
= entry_new(FALSE
);
306 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
307 e
->version
> ENTRY_VERSION
||
308 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
309 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
310 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
311 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
312 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
313 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
314 pa_tagstruct_gets(t
, &port
) < 0 ||
315 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
320 e
->port
= pa_xstrdup(port
);
322 for (i
= 0; i
< n_formats
; ++i
) {
323 pa_format_info
*f
= pa_format_info_new();
324 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
325 pa_format_info_free(f
);
328 pa_idxset_put(e
->formats
, f
, NULL
);
331 if (!pa_tagstruct_eof(t
))
334 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
335 pa_log_warn("Invalid channel map stored in database for device %s", name
);
339 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
340 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
344 pa_tagstruct_free(t
);
345 pa_datum_free(&data
);
351 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
356 pa_tagstruct_free(t
);
358 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
359 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
360 if ((e
= legacy_entry_read(u
, &data
))) {
361 pa_log_debug("Success. Saving new format for key: %s", name
);
362 if (entry_write(u
, name
, e
))
363 trigger_save(u
, PA_INVALID_INDEX
);
364 pa_datum_free(&data
);
367 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
370 pa_datum_free(&data
);
374 static struct entry
* entry_copy(const struct entry
*e
) {
380 r
= entry_new(FALSE
);
381 r
->version
= e
->version
;
382 r
->muted_valid
= e
->muted_valid
;
383 r
->volume_valid
= e
->volume_valid
;
384 r
->port_valid
= e
->port_valid
;
386 r
->channel_map
= e
->channel_map
;
387 r
->volume
= e
->volume
;
388 r
->port
= pa_xstrdup(e
->port
);
390 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
391 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
396 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
399 if (a
->port_valid
!= b
->port_valid
||
400 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
403 if (a
->muted_valid
!= b
->muted_valid
||
404 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
408 if (a
->volume_valid
!= b
->volume_valid
||
409 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
412 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
415 /** TODO: Compare a bit better */
420 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
421 struct userdata
*u
= userdata
;
422 struct entry
*entry
, *old
;
428 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
429 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
430 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
431 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
434 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
437 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
440 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
442 if ((old
= entry_read(u
, name
)))
443 entry
= entry_copy(old
);
445 entry
= entry_new(TRUE
);
447 if (sink
->save_volume
) {
448 entry
->channel_map
= sink
->channel_map
;
449 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
450 entry
->volume_valid
= TRUE
;
453 if (sink
->save_muted
) {
454 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
455 entry
->muted_valid
= TRUE
;
458 if (sink
->save_port
) {
459 pa_xfree(entry
->port
);
460 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
461 entry
->port_valid
= TRUE
;
467 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
469 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
472 name
= pa_sprintf_malloc("source:%s", source
->name
);
474 if ((old
= entry_read(u
, name
)))
475 entry
= entry_copy(old
);
477 entry
= entry_new(TRUE
);
479 if (source
->save_volume
) {
480 entry
->channel_map
= source
->channel_map
;
481 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
482 entry
->volume_valid
= TRUE
;
485 if (source
->save_muted
) {
486 entry
->muted
= pa_source_get_mute(source
, FALSE
);
487 entry
->muted_valid
= TRUE
;
490 if (source
->save_port
) {
491 pa_xfree(entry
->port
);
492 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
493 entry
->port_valid
= TRUE
;
501 if (entries_equal(old
, entry
)) {
511 pa_log_info("Storing volume/mute/port for device %s.", name
);
513 if (entry_write(u
, name
, entry
))
514 trigger_save(u
, idx
);
520 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
527 pa_assert(u
->restore_port
);
529 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
531 if ((e
= entry_read(u
, name
))) {
534 if (!new_data
->active_port
) {
535 pa_log_info("Restoring port for sink %s.", name
);
536 pa_sink_new_data_set_port(new_data
, e
->port
);
537 new_data
->save_port
= TRUE
;
539 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
550 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
557 pa_assert(u
->restore_volume
|| u
->restore_muted
);
559 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
561 if ((e
= entry_read(u
, name
))) {
563 if (u
->restore_volume
&& e
->volume_valid
) {
565 if (!new_data
->volume_is_set
) {
568 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
571 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
572 pa_sink_new_data_set_volume(new_data
, &v
);
574 new_data
->save_volume
= TRUE
;
576 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
579 if (u
->restore_muted
&& e
->muted_valid
) {
581 if (!new_data
->muted_is_set
) {
582 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
583 pa_sink_new_data_set_muted(new_data
, e
->muted
);
584 new_data
->save_muted
= TRUE
;
586 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
597 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
604 pa_assert(u
->restore_formats
);
606 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
608 if ((e
= entry_read(u
, name
))) {
610 if (!pa_sink_set_formats(sink
, e
->formats
))
611 pa_log_debug("Could not set format on sink %s", sink
->name
);
621 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
628 pa_assert(u
->restore_port
);
630 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
632 if ((e
= entry_read(u
, name
))) {
635 if (!new_data
->active_port
) {
636 pa_log_info("Restoring port for source %s.", name
);
637 pa_source_new_data_set_port(new_data
, e
->port
);
638 new_data
->save_port
= TRUE
;
640 pa_log_debug("Not restoring port for source %s, because already set.", name
);
651 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
658 pa_assert(u
->restore_volume
|| u
->restore_muted
);
660 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
662 if ((e
= entry_read(u
, name
))) {
664 if (u
->restore_volume
&& e
->volume_valid
) {
666 if (!new_data
->volume_is_set
) {
669 pa_log_info("Restoring volume for source %s.", new_data
->name
);
672 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
673 pa_source_new_data_set_volume(new_data
, &v
);
675 new_data
->save_volume
= TRUE
;
677 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
680 if (u
->restore_muted
&& e
->muted_valid
) {
682 if (!new_data
->muted_is_set
) {
683 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
684 pa_source_new_data_set_muted(new_data
, e
->muted
);
685 new_data
->save_muted
= TRUE
;
687 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
698 #define EXT_VERSION 1
700 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
708 pa_tagstruct_putu32(reply
, sink
->index
);
710 /* Read or create an entry */
711 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
712 if (!(e
= entry_read(u
, name
))) {
713 /* Fake a reply with PCM encoding supported */
714 pa_format_info
*f
= pa_format_info_new();
716 pa_tagstruct_putu8(reply
, 1);
717 f
->encoding
= PA_ENCODING_PCM
;
718 pa_tagstruct_put_format_info(reply
, f
);
720 pa_format_info_free(f
);
725 /* Write all the formats from the entry to the reply */
726 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
727 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
728 pa_tagstruct_put_format_info(reply
, f
);
734 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
737 pa_tagstruct
*reply
= NULL
;
746 if (pa_tagstruct_getu32(t
, &command
) < 0)
749 reply
= pa_tagstruct_new(NULL
, 0);
750 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
751 pa_tagstruct_putu32(reply
, tag
);
754 case SUBCOMMAND_TEST
: {
755 if (!pa_tagstruct_eof(t
))
758 pa_tagstruct_putu32(reply
, EXT_VERSION
);
762 case SUBCOMMAND_SUBSCRIBE
: {
766 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
767 !pa_tagstruct_eof(t
))
771 pa_idxset_put(u
->subscribed
, c
, NULL
);
773 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
778 case SUBCOMMAND_READ_SINK_FORMATS_ALL
: {
782 if (!pa_tagstruct_eof(t
))
785 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
786 read_sink_format_reply(u
, reply
, sink
);
791 case SUBCOMMAND_READ_SINK_FORMATS
: {
797 /* Get the sink index and the number of formats from the tagstruct */
798 if (pa_tagstruct_getu32(t
, &sink_index
) < 0)
801 if (!pa_tagstruct_eof(t
))
804 /* Now find our sink */
805 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
808 read_sink_format_reply(u
, reply
, sink
);
813 case SUBCOMMAND_SAVE_SINK_FORMATS
: {
819 uint8_t i
, n_formats
;
821 /* Get the sink index and the number of formats from the tagstruct */
822 if (pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
823 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
828 /* Now find our sink */
829 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
832 /* Read or create an entry */
833 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
834 if (!(e
= entry_read(u
, name
)))
835 e
= entry_new(FALSE
);
837 /* Clean out any saved formats */
838 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
839 e
->formats
= pa_idxset_new(NULL
, NULL
);
842 /* Read all the formats from our tagstruct */
843 for (i
= 0; i
< n_formats
; ++i
) {
844 pa_format_info
*f
= pa_format_info_new();
845 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
846 pa_format_info_free(f
);
850 pa_idxset_put(e
->formats
, f
, NULL
);
853 if (!pa_tagstruct_eof(t
)) {
859 if (pa_sink_set_formats(sink
, e
->formats
) && entry_write(u
, name
, e
))
860 trigger_save(u
, sink_index
);
862 pa_log_warn("Could not save format info for sink %s", sink
->name
);
874 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
880 pa_tagstruct_free(reply
);
885 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
890 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
894 int pa__init(pa_module
*m
) {
895 pa_modargs
*ma
= NULL
;
901 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
, restore_formats
= TRUE
;
905 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
906 pa_log("Failed to parse module arguments");
910 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
911 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
912 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0 ||
913 pa_modargs_get_value_boolean(ma
, "restore_formats", &restore_formats
) < 0) {
914 pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
918 if (!restore_muted
&& !restore_volume
&& !restore_port
&& !restore_formats
)
919 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
921 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
924 u
->restore_volume
= restore_volume
;
925 u
->restore_muted
= restore_muted
;
926 u
->restore_port
= restore_port
;
927 u
->restore_formats
= restore_formats
;
929 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
931 u
->protocol
= pa_native_protocol_get(m
->core
);
932 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
934 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
);
936 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
939 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
);
940 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
);
943 if (restore_muted
|| restore_volume
) {
944 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
);
945 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
);
949 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
);
951 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
954 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
955 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
960 pa_log_info("Successfully opened database file '%s'.", fname
);
963 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
964 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
966 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
967 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
981 void pa__done(pa_module
*m
) {
986 if (!(u
= m
->userdata
))
990 pa_subscription_free(u
->subscription
);
992 if (u
->sink_fixate_hook_slot
)
993 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
994 if (u
->source_fixate_hook_slot
)
995 pa_hook_slot_free(u
->source_fixate_hook_slot
);
996 if (u
->sink_new_hook_slot
)
997 pa_hook_slot_free(u
->sink_new_hook_slot
);
998 if (u
->source_new_hook_slot
)
999 pa_hook_slot_free(u
->source_new_hook_slot
);
1000 if (u
->sink_put_hook_slot
)
1001 pa_hook_slot_free(u
->sink_put_hook_slot
);
1003 if (u
->connection_unlink_hook_slot
)
1004 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1006 if (u
->save_time_event
)
1007 u
->core
->mainloop
->time_free(u
->save_time_event
);
1010 pa_database_close(u
->database
);
1013 pa_native_protocol_remove_ext(u
->protocol
, m
);
1014 pa_native_protocol_unref(u
->protocol
);
1018 pa_idxset_free(u
->subscribed
, NULL
, NULL
);