2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include <sys/types.h>
34 #include <pulse/xmalloc.h>
35 #include <pulse/volume.h>
36 #include <pulse/timeval.h>
37 #include <pulse/util.h>
38 #include <pulse/rtclock.h>
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/module.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/modargs.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/core-subscribe.h>
46 #include <pulsecore/sink-input.h>
47 #include <pulsecore/source-output.h>
48 #include <pulsecore/namereg.h>
49 #include <pulsecore/database.h>
50 #include <pulsecore/tagstruct.h>
52 #include "module-device-restore-symdef.h"
54 PA_MODULE_AUTHOR("Lennart Poettering");
55 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
56 PA_MODULE_VERSION(PACKAGE_VERSION
);
57 PA_MODULE_LOAD_ONCE(TRUE
);
59 "restore_port=<Save/restore port?> "
60 "restore_volume=<Save/restore volumes?> "
61 "restore_muted=<Save/restore muted states?>");
63 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
65 static const char* const valid_modargs
[] = {
75 pa_subscription
*subscription
;
78 *sink_fixate_hook_slot
,
79 *source_new_hook_slot
,
80 *source_fixate_hook_slot
;
81 pa_time_event
*save_time_event
;
82 pa_database
*database
;
84 pa_bool_t restore_volume
:1;
85 pa_bool_t restore_muted
:1;
86 pa_bool_t restore_port
:1;
89 #define ENTRY_VERSION 3
93 pa_bool_t muted_valid
, volume_valid
, port_valid
;
95 pa_channel_map channel_map
;
100 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
101 struct userdata
*u
= userdata
;
107 pa_assert(e
== u
->save_time_event
);
108 u
->core
->mainloop
->time_free(u
->save_time_event
);
109 u
->save_time_event
= NULL
;
111 pa_database_sync(u
->database
);
112 pa_log_info("Synced.");
115 static struct entry
* entry_new(void) {
116 struct entry
*r
= pa_xnew0(struct entry
, 1);
117 r
->version
= ENTRY_VERSION
;
121 static void entry_free(struct entry
* e
) {
128 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
130 struct entry
*e
= NULL
;
131 pa_tagstruct
*t
= NULL
;
137 key
.data
= (char*) name
;
138 key
.size
= strlen(name
);
142 if (!pa_database_get(u
->database
, &key
, &data
))
145 t
= pa_tagstruct_new(data
.data
, data
.size
);
148 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
149 e
->version
> ENTRY_VERSION
||
150 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
151 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
152 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
153 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
154 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
155 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
156 pa_tagstruct_gets(t
, &port
) < 0) {
161 e
->port
= pa_xstrdup(port
);
163 if (!pa_tagstruct_eof(t
))
166 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
167 pa_log_warn("Invalid channel map stored in database for device %s", name
);
171 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
172 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
176 pa_tagstruct_free(t
);
177 pa_datum_free(&data
);
183 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
188 pa_tagstruct_free(t
);
189 pa_datum_free(&data
);
193 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
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
);
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 static struct entry
* entry_copy(const struct entry
*e
) {
230 r
->port
= pa_xstrdup(e
->port
);
234 static void trigger_save(struct userdata
*u
) {
235 if (u
->save_time_event
)
238 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
241 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
244 if (a
->port_valid
!= b
->port_valid
||
245 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
248 if (a
->muted_valid
!= b
->muted_valid
||
249 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
253 if (a
->volume_valid
!= b
->volume_valid
||
254 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
260 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
261 struct userdata
*u
= userdata
;
262 struct entry
*entry
, *old
;
268 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
269 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
270 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
271 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
274 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
277 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
280 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
282 if ((old
= entry_read(u
, name
)))
283 entry
= entry_copy(old
);
287 if (sink
->save_volume
) {
288 entry
->channel_map
= sink
->channel_map
;
289 entry
->volume
= *pa_sink_get_volume(sink
, FALSE
);
290 entry
->volume_valid
= TRUE
;
293 if (sink
->save_muted
) {
294 entry
->muted
= pa_sink_get_mute(sink
, FALSE
);
295 entry
->muted_valid
= TRUE
;
298 if (sink
->save_port
) {
299 pa_xfree(entry
->port
);
300 entry
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
301 entry
->port_valid
= TRUE
;
307 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
309 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
312 name
= pa_sprintf_malloc("source:%s", source
->name
);
314 if ((old
= entry_read(u
, name
)))
315 entry
= entry_copy(old
);
319 if (source
->save_volume
) {
320 entry
->channel_map
= source
->channel_map
;
321 entry
->volume
= *pa_source_get_volume(source
, FALSE
);
322 entry
->volume_valid
= TRUE
;
325 if (source
->save_muted
) {
326 entry
->muted
= pa_source_get_mute(source
, FALSE
);
327 entry
->muted_valid
= TRUE
;
330 if (source
->save_port
) {
331 pa_xfree(entry
->port
);
332 entry
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
333 entry
->port_valid
= TRUE
;
341 if (entries_equal(old
, entry
)) {
351 pa_log_info("Storing volume/mute/port for device %s.", name
);
353 if (entry_write(u
, name
, entry
))
360 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
367 pa_assert(u
->restore_port
);
369 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
371 if ((e
= entry_read(u
, name
))) {
374 if (!new_data
->active_port
) {
375 pa_log_info("Restoring port for sink %s.", name
);
376 pa_sink_new_data_set_port(new_data
, e
->port
);
377 new_data
->save_port
= TRUE
;
379 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
390 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
397 pa_assert(u
->restore_volume
|| u
->restore_muted
);
399 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
401 if ((e
= entry_read(u
, name
))) {
403 if (u
->restore_volume
&& e
->volume_valid
) {
405 if (!new_data
->volume_is_set
) {
408 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
411 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
412 pa_sink_new_data_set_volume(new_data
, &v
);
414 new_data
->save_volume
= TRUE
;
416 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
419 if (u
->restore_muted
&& e
->muted_valid
) {
421 if (!new_data
->muted_is_set
) {
422 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
423 pa_sink_new_data_set_muted(new_data
, e
->muted
);
424 new_data
->save_muted
= TRUE
;
426 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
437 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
444 pa_assert(u
->restore_port
);
446 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
448 if ((e
= entry_read(u
, name
))) {
451 if (!new_data
->active_port
) {
452 pa_log_info("Restoring port for source %s.", name
);
453 pa_source_new_data_set_port(new_data
, e
->port
);
454 new_data
->save_port
= TRUE
;
456 pa_log_debug("Not restoring port for source %s, because already set.", name
);
467 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
474 pa_assert(u
->restore_volume
|| u
->restore_muted
);
476 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
478 if ((e
= entry_read(u
, name
))) {
480 if (u
->restore_volume
&& e
->volume_valid
) {
482 if (!new_data
->volume_is_set
) {
485 pa_log_info("Restoring volume for source %s.", new_data
->name
);
488 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
489 pa_source_new_data_set_volume(new_data
, &v
);
491 new_data
->save_volume
= TRUE
;
493 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
496 if (u
->restore_muted
&& e
->muted_valid
) {
498 if (!new_data
->muted_is_set
) {
499 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
500 pa_source_new_data_set_muted(new_data
, e
->muted
);
501 new_data
->save_muted
= TRUE
;
503 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
514 int pa__init(pa_module
*m
) {
515 pa_modargs
*ma
= NULL
;
521 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
;
525 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
526 pa_log("Failed to parse module arguments");
530 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
531 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
532 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0) {
533 pa_log("restore_port=, restore_volume= and restore_muted= expect boolean arguments");
537 if (!restore_muted
&& !restore_volume
&& !restore_port
)
538 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
540 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
543 u
->restore_volume
= restore_volume
;
544 u
->restore_muted
= restore_muted
;
545 u
->restore_port
= restore_port
;
547 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
550 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
);
551 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
);
554 if (restore_muted
|| restore_volume
) {
555 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
);
556 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
);
559 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
562 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
563 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
568 pa_log_info("Successfully opened database file '%s'.", fname
);
571 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
572 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
574 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
575 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
589 void pa__done(pa_module
*m
) {
594 if (!(u
= m
->userdata
))
598 pa_subscription_free(u
->subscription
);
600 if (u
->sink_fixate_hook_slot
)
601 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
602 if (u
->source_fixate_hook_slot
)
603 pa_hook_slot_free(u
->source_fixate_hook_slot
);
604 if (u
->sink_new_hook_slot
)
605 pa_hook_slot_free(u
->sink_new_hook_slot
);
606 if (u
->source_new_hook_slot
)
607 pa_hook_slot_free(u
->source_new_hook_slot
);
609 if (u
->save_time_event
)
610 u
->core
->mainloop
->time_free(u
->save_time_event
);
613 pa_database_close(u
->database
);