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
,
88 *source_new_hook_slot
,
89 *source_fixate_hook_slot
,
90 *source_port_hook_slot
,
91 *connection_unlink_hook_slot
;
92 pa_time_event
*save_time_event
;
93 pa_database
*database
;
95 pa_native_protocol
*protocol
;
96 pa_idxset
*subscribed
;
98 pa_bool_t restore_volume
:1;
99 pa_bool_t restore_muted
:1;
100 pa_bool_t restore_port
:1;
101 pa_bool_t restore_formats
:1;
104 /* Protocol extension commands */
107 SUBCOMMAND_SUBSCRIBE
,
109 SUBCOMMAND_READ_FORMATS_ALL
,
110 SUBCOMMAND_READ_FORMATS
,
111 SUBCOMMAND_SAVE_FORMATS
115 #define ENTRY_VERSION 1
119 pa_bool_t port_valid
;
123 #define PERPORTENTRY_VERSION 1
125 struct perportentry
{
127 pa_bool_t muted_valid
, volume_valid
;
129 pa_channel_map channel_map
;
134 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
135 struct userdata
*u
= userdata
;
141 pa_assert(e
== u
->save_time_event
);
142 u
->core
->mainloop
->time_free(u
->save_time_event
);
143 u
->save_time_event
= NULL
;
145 pa_database_sync(u
->database
);
146 pa_log_info("Synced.");
149 static void trigger_save(struct userdata
*u
, pa_device_type_t type
, uint32_t sink_idx
) {
150 pa_native_connection
*c
;
153 if (sink_idx
!= PA_INVALID_INDEX
) {
154 for (c
= pa_idxset_first(u
->subscribed
, &idx
); c
; c
= pa_idxset_next(u
->subscribed
, &idx
)) {
157 t
= pa_tagstruct_new(NULL
, 0);
158 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
159 pa_tagstruct_putu32(t
, 0);
160 pa_tagstruct_putu32(t
, u
->module
->index
);
161 pa_tagstruct_puts(t
, u
->module
->name
);
162 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
163 pa_tagstruct_putu32(t
, type
);
164 pa_tagstruct_putu32(t
, sink_idx
);
166 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
170 if (u
->save_time_event
)
173 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
177 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
178 /* Some forward declarations */
179 static pa_bool_t
legacy_entry_read(struct userdata
*u
, pa_datum
*data
, struct entry
**entry
, struct perportentry
**perportentry
);
180 static pa_bool_t
perportentry_write(struct userdata
*u
, const char *name
, const struct perportentry
*e
);
181 static void perportentry_free(struct perportentry
* e
);
184 static struct entry
* entry_new() {
185 struct entry
*r
= pa_xnew0(struct entry
, 1);
186 r
->version
= ENTRY_VERSION
;
190 static void entry_free(struct entry
* e
) {
197 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
206 t
= pa_tagstruct_new(NULL
, 0);
207 pa_tagstruct_putu8(t
, e
->version
);
208 pa_tagstruct_put_boolean(t
, e
->port_valid
);
209 pa_tagstruct_puts(t
, e
->port
);
211 key
.data
= (char *) name
;
212 key
.size
= strlen(name
);
214 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
216 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
218 pa_tagstruct_free(t
);
223 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
225 struct entry
*e
= NULL
;
226 pa_tagstruct
*t
= NULL
;
232 key
.data
= (char*) name
;
233 key
.size
= strlen(name
);
237 if (!pa_database_get(u
->database
, &key
, &data
))
240 t
= pa_tagstruct_new(data
.data
, data
.size
);
241 e
= entry_new(FALSE
);
243 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
244 e
->version
> ENTRY_VERSION
||
245 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
246 pa_tagstruct_gets(t
, &port
) < 0) {
251 if (!pa_tagstruct_eof(t
))
254 e
->port
= pa_xstrdup(port
);
256 pa_tagstruct_free(t
);
257 pa_datum_free(&data
);
263 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
268 pa_tagstruct_free(t
);
270 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
272 struct perportentry
*ppe
;
273 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
274 if (legacy_entry_read(u
, &data
, &e
, &ppe
)) {
275 pa_bool_t written
= FALSE
;
276 pa_device_port
*dport
;
280 pa_log_debug("Success. Saving new format for key: %s", name
);
281 written
= entry_write(u
, name
, e
);
283 /* Now convert the legacy entry into per-port entries */
284 if (0 == strncmp("sink:", name
, 5)) {
287 if ((sink
= pa_namereg_get(u
->core
, name
+5, PA_NAMEREG_SINK
))) {
289 PA_HASHMAP_FOREACH(dport
, sink
->ports
, state
) {
290 ppename
= pa_sprintf_malloc("%s:%s", name
, dport
->name
);
291 written
= perportentry_write(u
, ppename
, ppe
) || written
;
295 ppename
= pa_sprintf_malloc("%s:%s", name
, "null");
296 written
= perportentry_write(u
, ppename
, ppe
) || written
;
300 } else if (0 == strncmp("source:", name
, 7)) {
303 if ((source
= pa_namereg_get(u
->core
, name
+7, PA_NAMEREG_SOURCE
))) {
305 PA_HASHMAP_FOREACH(dport
, source
->ports
, state
) {
306 ppename
= pa_sprintf_malloc("%s:%s", name
, dport
->name
);
307 written
= perportentry_write(u
, ppename
, ppe
) || written
;
311 ppename
= pa_sprintf_malloc("%s:%s", name
, "null");
312 written
= perportentry_write(u
, ppename
, ppe
) || written
;
317 perportentry_free(ppe
);
320 /* NB The device type doesn't matter when we pass in an invalid index. */
321 trigger_save(u
, PA_DEVICE_TYPE_SINK
, PA_INVALID_INDEX
);
323 pa_datum_free(&data
);
326 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
330 pa_datum_free(&data
);
334 static struct entry
* entry_copy(const struct entry
*e
) {
339 r
->version
= e
->version
;
340 r
->port_valid
= e
->port_valid
;
341 r
->port
= pa_xstrdup(e
->port
);
346 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
350 if (a
->port_valid
!= b
->port_valid
||
351 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
357 static struct perportentry
* perportentry_new(pa_bool_t add_pcm_format
) {
358 struct perportentry
*r
= pa_xnew0(struct perportentry
, 1);
359 r
->version
= PERPORTENTRY_VERSION
;
360 r
->formats
= pa_idxset_new(NULL
, NULL
);
361 if (add_pcm_format
) {
362 pa_format_info
*f
= pa_format_info_new();
363 f
->encoding
= PA_ENCODING_PCM
;
364 pa_idxset_put(r
->formats
, f
, NULL
);
369 static void perportentry_free(struct perportentry
* e
) {
372 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
376 static pa_bool_t
perportentry_write(struct userdata
*u
, const char *name
, const struct perportentry
*e
) {
388 n_formats
= pa_idxset_size(e
->formats
);
389 pa_assert(n_formats
> 0);
391 t
= pa_tagstruct_new(NULL
, 0);
392 pa_tagstruct_putu8(t
, e
->version
);
393 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
394 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
395 pa_tagstruct_put_cvolume(t
, &e
->volume
);
396 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
397 pa_tagstruct_put_boolean(t
, e
->muted
);
398 pa_tagstruct_putu8(t
, n_formats
);
400 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
401 pa_tagstruct_put_format_info(t
, f
);
404 key
.data
= (char *) name
;
405 key
.size
= strlen(name
);
407 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
409 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
411 pa_tagstruct_free(t
);
416 static struct perportentry
* perportentry_read(struct userdata
*u
, const char *name
) {
418 struct perportentry
*e
= NULL
;
419 pa_tagstruct
*t
= NULL
;
420 uint8_t i
, n_formats
;
425 key
.data
= (char*) name
;
426 key
.size
= strlen(name
);
430 if (!pa_database_get(u
->database
, &key
, &data
))
433 t
= pa_tagstruct_new(data
.data
, data
.size
);
434 e
= perportentry_new(FALSE
);
436 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
437 e
->version
> PERPORTENTRY_VERSION
||
438 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
439 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
440 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
441 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
442 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
443 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
448 for (i
= 0; i
< n_formats
; ++i
) {
449 pa_format_info
*f
= pa_format_info_new();
450 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
451 pa_format_info_free(f
);
454 pa_idxset_put(e
->formats
, f
, NULL
);
457 if (!pa_tagstruct_eof(t
))
460 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
461 pa_log_warn("Invalid channel map stored in database for device %s", name
);
465 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
466 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
470 pa_tagstruct_free(t
);
471 pa_datum_free(&data
);
477 pa_log_debug("Database contains invalid data for key: %s", name
);
480 perportentry_free(e
);
482 pa_tagstruct_free(t
);
484 pa_datum_free(&data
);
488 static struct perportentry
* perportentry_copy(const struct perportentry
*e
) {
489 struct perportentry
* r
;
494 r
= perportentry_new(FALSE
);
495 r
->version
= e
->version
;
496 r
->muted_valid
= e
->muted_valid
;
497 r
->volume_valid
= e
->volume_valid
;
499 r
->channel_map
= e
->channel_map
;
500 r
->volume
= e
->volume
;
502 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
503 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
508 static pa_bool_t
perportentries_equal(const struct perportentry
*a
, const struct perportentry
*b
) {
513 if (a
->muted_valid
!= b
->muted_valid
||
514 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
518 if (a
->volume_valid
!= b
->volume_valid
||
519 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
522 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
525 /** TODO: Compare a bit better */
530 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
532 #define LEGACY_ENTRY_VERSION 2
533 static pa_bool_t
legacy_entry_read(struct userdata
*u
, pa_datum
*data
, struct entry
**entry
, struct perportentry
**perportentry
) {
534 struct legacy_entry
{
536 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
538 pa_channel_map channel_map
;
540 char port
[PA_NAME_MAX
];
542 struct legacy_entry
*le
;
547 pa_assert(perportentry
);
549 if (data
->size
!= sizeof(struct legacy_entry
)) {
550 pa_log_debug("Size does not match.");
554 le
= (struct legacy_entry
*)data
->data
;
556 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
557 pa_log_debug("Version mismatch.");
561 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
562 pa_log_warn("Port has missing NUL byte.");
566 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
567 pa_log_warn("Invalid channel map.");
571 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
572 pa_log_warn("Volume and channel map don't match.");
576 *entry
= entry_new();
577 (*entry
)->port_valid
= le
->port_valid
;
578 (*entry
)->port
= pa_xstrdup(le
->port
);
580 *perportentry
= perportentry_new(TRUE
);
581 (*perportentry
)->muted_valid
= le
->muted_valid
;
582 (*perportentry
)->volume_valid
= le
->volume_valid
;
583 (*perportentry
)->muted
= le
->muted
;
584 (*perportentry
)->channel_map
= le
->channel_map
;
585 (*perportentry
)->volume
= le
->volume
;
591 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
592 struct userdata
*u
= userdata
;
593 struct entry
*e
, *olde
;
594 struct perportentry
*ppe
, *oldppe
;
595 char *ename
, *ppename
;
596 pa_device_type_t type
;
597 pa_bool_t written
= FALSE
;
602 if (t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
) &&
603 t
!= (PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_CHANGE
) &&
604 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
) &&
605 t
!= (PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_CHANGE
))
608 if ((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SINK
) {
611 if (!(sink
= pa_idxset_get_by_index(c
->sinks
, idx
)))
614 type
= PA_DEVICE_TYPE_SINK
;
616 ename
= pa_sprintf_malloc("sink:%s", sink
->name
);
617 if ((olde
= entry_read(u
, ename
)))
618 e
= entry_copy(olde
);
622 if (sink
->save_port
) {
624 e
->port
= pa_xstrdup(sink
->active_port
? sink
->active_port
->name
: "");
625 e
->port_valid
= TRUE
;
628 ppename
= pa_sprintf_malloc("sink:%s:%s", sink
->name
, (sink
->active_port
? sink
->active_port
->name
: "null"));
629 if ((oldppe
= perportentry_read(u
, ppename
)))
630 ppe
= perportentry_copy(oldppe
);
632 ppe
= perportentry_new(TRUE
);
634 if (sink
->save_volume
) {
635 ppe
->channel_map
= sink
->channel_map
;
636 ppe
->volume
= *pa_sink_get_volume(sink
, FALSE
);
637 ppe
->volume_valid
= TRUE
;
640 if (sink
->save_muted
) {
641 ppe
->muted
= pa_sink_get_mute(sink
, FALSE
);
642 ppe
->muted_valid
= TRUE
;
647 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
649 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
652 type
= PA_DEVICE_TYPE_SOURCE
;
654 ename
= pa_sprintf_malloc("source:%s", source
->name
);
655 if ((olde
= entry_read(u
, ename
)))
656 e
= entry_copy(olde
);
660 if (source
->save_port
) {
662 e
->port
= pa_xstrdup(source
->active_port
? source
->active_port
->name
: "");
663 e
->port_valid
= TRUE
;
666 ppename
= pa_sprintf_malloc("source:%s:%s", source
->name
, (source
->active_port
? source
->active_port
->name
: "null"));
667 if ((oldppe
= perportentry_read(u
, ppename
)))
668 ppe
= perportentry_copy(oldppe
);
670 ppe
= perportentry_new(TRUE
);
672 if (source
->save_volume
) {
673 ppe
->channel_map
= source
->channel_map
;
674 ppe
->volume
= *pa_source_get_volume(source
, FALSE
);
675 ppe
->volume_valid
= TRUE
;
678 if (source
->save_muted
) {
679 ppe
->muted
= pa_source_get_mute(source
, FALSE
);
680 ppe
->muted_valid
= TRUE
;
689 if (entries_equal(olde
, e
)) {
698 pa_log_info("Storing port for device %s.", ename
);
700 written
= entry_write(u
, ename
, e
);
711 if (perportentries_equal(oldppe
, ppe
)) {
712 perportentry_free(oldppe
);
713 perportentry_free(ppe
);
716 perportentry_free(oldppe
);
720 pa_log_info("Storing volume/mute for device+port %s.", ppename
);
722 written
= perportentry_write(u
, ppename
, ppe
) || written
;
724 perportentry_free(ppe
);
729 trigger_save(u
, type
, idx
);
732 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
739 pa_assert(u
->restore_port
);
741 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
743 if ((e
= entry_read(u
, name
))) {
746 if (!new_data
->active_port
) {
747 pa_log_info("Restoring port for sink %s.", name
);
748 pa_sink_new_data_set_port(new_data
, e
->port
);
749 new_data
->save_port
= TRUE
;
751 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
762 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
764 struct perportentry
*e
;
769 pa_assert(u
->restore_volume
|| u
->restore_muted
);
771 name
= pa_sprintf_malloc("sink:%s:%s", new_data
->name
, (new_data
->active_port
? new_data
->active_port
: "null"));
773 if ((e
= perportentry_read(u
, name
))) {
775 if (u
->restore_volume
&& e
->volume_valid
) {
777 if (!new_data
->volume_is_set
) {
780 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
783 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
784 pa_sink_new_data_set_volume(new_data
, &v
);
786 new_data
->save_volume
= TRUE
;
788 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data
->name
);
791 if (u
->restore_muted
&& e
->muted_valid
) {
793 if (!new_data
->muted_is_set
) {
794 pa_log_info("Restoring mute state for sink %s.", new_data
->name
);
795 pa_sink_new_data_set_muted(new_data
, e
->muted
);
796 new_data
->save_muted
= TRUE
;
798 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
801 perportentry_free(e
);
809 static pa_hook_result_t
sink_port_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
811 struct perportentry
*e
;
816 pa_assert(u
->restore_volume
|| u
->restore_muted
);
818 name
= pa_sprintf_malloc("sink:%s:%s", sink
->name
, (sink
->active_port
? sink
->active_port
->name
: "null"));
820 if ((e
= perportentry_read(u
, name
))) {
822 if (u
->restore_volume
&& e
->volume_valid
) {
826 pa_log_info("Restoring volume for sink %s.", sink
->name
);
829 pa_cvolume_remap(&v
, &e
->channel_map
, &sink
->channel_map
);
830 pa_sink_set_volume(sink
, &v
, TRUE
, FALSE
);
831 sink
->save_volume
= TRUE
;
834 if (u
->restore_muted
&& e
->muted_valid
) {
836 pa_log_info("Restoring mute state for sink %s.", sink
->name
);
837 pa_sink_set_mute(sink
, e
->muted
, FALSE
);
838 sink
->save_muted
= TRUE
;
841 perportentry_free(e
);
849 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
851 struct perportentry
*e
;
856 pa_assert(u
->restore_formats
);
858 name
= pa_sprintf_malloc("sink:%s:%s", sink
->name
, (sink
->active_port
? sink
->active_port
->name
: "null"));
860 if ((e
= perportentry_read(u
, name
))) {
862 if (!pa_sink_set_formats(sink
, e
->formats
))
863 pa_log_debug("Could not set format on sink %s", sink
->name
);
865 perportentry_free(e
);
873 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
880 pa_assert(u
->restore_port
);
882 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
884 if ((e
= entry_read(u
, name
))) {
887 if (!new_data
->active_port
) {
888 pa_log_info("Restoring port for source %s.", name
);
889 pa_source_new_data_set_port(new_data
, e
->port
);
890 new_data
->save_port
= TRUE
;
892 pa_log_debug("Not restoring port for source %s, because already set.", name
);
903 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
905 struct perportentry
*e
;
910 pa_assert(u
->restore_volume
|| u
->restore_muted
);
912 name
= pa_sprintf_malloc("source:%s:%s", new_data
->name
, (new_data
->active_port
? new_data
->active_port
: "null"));
914 if ((e
= perportentry_read(u
, name
))) {
916 if (u
->restore_volume
&& e
->volume_valid
) {
918 if (!new_data
->volume_is_set
) {
921 pa_log_info("Restoring volume for source %s.", new_data
->name
);
924 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
925 pa_source_new_data_set_volume(new_data
, &v
);
927 new_data
->save_volume
= TRUE
;
929 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
932 if (u
->restore_muted
&& e
->muted_valid
) {
934 if (!new_data
->muted_is_set
) {
935 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
936 pa_source_new_data_set_muted(new_data
, e
->muted
);
937 new_data
->save_muted
= TRUE
;
939 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
942 perportentry_free(e
);
950 static pa_hook_result_t
source_port_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
952 struct perportentry
*e
;
957 pa_assert(u
->restore_volume
|| u
->restore_muted
);
959 name
= pa_sprintf_malloc("source:%s:%s", source
->name
, (source
->active_port
? source
->active_port
->name
: "null"));
961 if ((e
= perportentry_read(u
, name
))) {
963 if (u
->restore_volume
&& e
->volume_valid
) {
967 pa_log_info("Restoring volume for source %s.", source
->name
);
970 pa_cvolume_remap(&v
, &e
->channel_map
, &source
->channel_map
);
971 pa_source_set_volume(source
, &v
, TRUE
, FALSE
);
972 source
->save_volume
= TRUE
;
975 if (u
->restore_muted
&& e
->muted_valid
) {
977 pa_log_info("Restoring mute state for source %s.", source
->name
);
978 pa_source_set_mute(source
, e
->muted
, FALSE
);
979 source
->save_muted
= TRUE
;
982 perportentry_free(e
);
990 #define EXT_VERSION 1
992 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
993 struct perportentry
*e
;
1000 pa_tagstruct_putu32(reply
, PA_DEVICE_TYPE_SINK
);
1001 pa_tagstruct_putu32(reply
, sink
->index
);
1003 /* Read or create an entry */
1004 name
= pa_sprintf_malloc("sink:%s:%s", sink
->name
, (sink
->active_port
? sink
->active_port
->name
: "null"));
1005 if (!(e
= perportentry_read(u
, name
))) {
1006 /* Fake a reply with PCM encoding supported */
1007 pa_format_info
*f
= pa_format_info_new();
1009 pa_tagstruct_putu8(reply
, 1);
1010 f
->encoding
= PA_ENCODING_PCM
;
1011 pa_tagstruct_put_format_info(reply
, f
);
1013 pa_format_info_free(f
);
1018 /* Write all the formats from the entry to the reply */
1019 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
1020 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
1021 pa_tagstruct_put_format_info(reply
, f
);
1027 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
1030 pa_tagstruct
*reply
= NULL
;
1039 if (pa_tagstruct_getu32(t
, &command
) < 0)
1042 reply
= pa_tagstruct_new(NULL
, 0);
1043 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
1044 pa_tagstruct_putu32(reply
, tag
);
1047 case SUBCOMMAND_TEST
: {
1048 if (!pa_tagstruct_eof(t
))
1051 pa_tagstruct_putu32(reply
, EXT_VERSION
);
1055 case SUBCOMMAND_SUBSCRIBE
: {
1059 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1060 !pa_tagstruct_eof(t
))
1064 pa_idxset_put(u
->subscribed
, c
, NULL
);
1066 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1071 case SUBCOMMAND_READ_FORMATS_ALL
: {
1075 if (!pa_tagstruct_eof(t
))
1078 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
1079 read_sink_format_reply(u
, reply
, sink
);
1084 case SUBCOMMAND_READ_FORMATS
: {
1085 pa_device_type_t type
;
1086 uint32_t sink_index
;
1091 /* Get the sink index and the number of formats from the tagstruct */
1092 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
1093 pa_tagstruct_getu32(t
, &sink_index
) < 0)
1096 if (type
!= PA_DEVICE_TYPE_SINK
) {
1097 pa_log("Device format reading is only supported on sinks");
1101 if (!pa_tagstruct_eof(t
))
1104 /* Now find our sink */
1105 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
1108 read_sink_format_reply(u
, reply
, sink
);
1113 case SUBCOMMAND_SAVE_FORMATS
: {
1115 struct perportentry
*e
;
1116 pa_device_type_t type
;
1117 uint32_t sink_index
;
1120 uint8_t i
, n_formats
;
1122 /* Get the sink index and the number of formats from the tagstruct */
1123 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
1124 pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
1125 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
1130 if (type
!= PA_DEVICE_TYPE_SINK
) {
1131 pa_log("Device format saving is only supported on sinks");
1135 /* Now find our sink */
1136 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
))) {
1137 pa_log("Could not find sink #%d", sink_index
);
1141 /* Read or create an entry */
1142 name
= pa_sprintf_malloc("sink:%s:%s", sink
->name
, (sink
->active_port
? sink
->active_port
->name
: "null"));
1143 if (!(e
= perportentry_read(u
, name
)))
1144 e
= perportentry_new(FALSE
);
1146 /* Clean out any saved formats */
1147 pa_idxset_free(e
->formats
, (pa_free2_cb_t
) pa_format_info_free2
, NULL
);
1148 e
->formats
= pa_idxset_new(NULL
, NULL
);
1151 /* Read all the formats from our tagstruct */
1152 for (i
= 0; i
< n_formats
; ++i
) {
1153 pa_format_info
*f
= pa_format_info_new();
1154 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
1155 pa_format_info_free(f
);
1159 pa_idxset_put(e
->formats
, f
, NULL
);
1162 if (!pa_tagstruct_eof(t
)) {
1163 perportentry_free(e
);
1168 if (pa_sink_set_formats(sink
, e
->formats
) && perportentry_write(u
, name
, e
))
1169 trigger_save(u
, type
, sink_index
);
1171 pa_log_warn("Could not save format info for sink %s", sink
->name
);
1174 perportentry_free(e
);
1183 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1189 pa_tagstruct_free(reply
);
1194 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1199 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1203 int pa__init(pa_module
*m
) {
1204 pa_modargs
*ma
= NULL
;
1210 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
, restore_formats
= TRUE
;
1214 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1215 pa_log("Failed to parse module arguments");
1219 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
1220 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
1221 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0 ||
1222 pa_modargs_get_value_boolean(ma
, "restore_formats", &restore_formats
) < 0) {
1223 pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
1227 if (!restore_muted
&& !restore_volume
&& !restore_port
&& !restore_formats
)
1228 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
1230 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1233 u
->restore_volume
= restore_volume
;
1234 u
->restore_muted
= restore_muted
;
1235 u
->restore_port
= restore_port
;
1236 u
->restore_formats
= restore_formats
;
1238 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1240 u
->protocol
= pa_native_protocol_get(m
->core
);
1241 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1243 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
);
1245 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1248 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
);
1249 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
);
1252 if (restore_muted
|| restore_volume
) {
1253 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
);
1254 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
);
1256 u
->sink_port_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_PORT_CHANGED
], PA_HOOK_EARLY
, (pa_hook_cb_t
) sink_port_hook_callback
, u
);
1257 u
->source_port_hook_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_PORT_CHANGED
], PA_HOOK_EARLY
, (pa_hook_cb_t
) source_port_hook_callback
, u
);
1260 if (restore_formats
)
1261 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
);
1263 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
1266 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1267 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1272 pa_log_info("Successfully opened database file '%s'.", fname
);
1275 for (sink
= pa_idxset_first(m
->core
->sinks
, &idx
); sink
; sink
= pa_idxset_next(m
->core
->sinks
, &idx
))
1276 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1278 for (source
= pa_idxset_first(m
->core
->sources
, &idx
); source
; source
= pa_idxset_next(m
->core
->sources
, &idx
))
1279 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1281 pa_modargs_free(ma
);
1288 pa_modargs_free(ma
);
1293 void pa__done(pa_module
*m
) {
1298 if (!(u
= m
->userdata
))
1301 if (u
->subscription
)
1302 pa_subscription_free(u
->subscription
);
1304 if (u
->sink_fixate_hook_slot
)
1305 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
1306 if (u
->source_fixate_hook_slot
)
1307 pa_hook_slot_free(u
->source_fixate_hook_slot
);
1308 if (u
->sink_new_hook_slot
)
1309 pa_hook_slot_free(u
->sink_new_hook_slot
);
1310 if (u
->source_new_hook_slot
)
1311 pa_hook_slot_free(u
->source_new_hook_slot
);
1312 if (u
->sink_port_hook_slot
)
1313 pa_hook_slot_free(u
->sink_port_hook_slot
);
1314 if (u
->source_port_hook_slot
)
1315 pa_hook_slot_free(u
->source_port_hook_slot
);
1316 if (u
->sink_put_hook_slot
)
1317 pa_hook_slot_free(u
->sink_put_hook_slot
);
1319 if (u
->connection_unlink_hook_slot
)
1320 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1322 if (u
->save_time_event
)
1323 u
->core
->mainloop
->time_free(u
->save_time_event
);
1326 pa_database_close(u
->database
);
1329 pa_native_protocol_remove_ext(u
->protocol
, m
);
1330 pa_native_protocol_unref(u
->protocol
);
1334 pa_idxset_free(u
->subscribed
, NULL
, NULL
);