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
114 #define ENTRY_VERSION 1
118 pa_bool_t port_valid
;
122 #define PERPORTENTRY_VERSION 1
124 struct perportentry
{
126 pa_bool_t muted_valid
, volume_valid
;
128 pa_channel_map channel_map
;
133 static void save_time_callback(pa_mainloop_api
*a
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
134 struct userdata
*u
= userdata
;
140 pa_assert(e
== u
->save_time_event
);
141 u
->core
->mainloop
->time_free(u
->save_time_event
);
142 u
->save_time_event
= NULL
;
144 pa_database_sync(u
->database
);
145 pa_log_info("Synced.");
148 static void trigger_save(struct userdata
*u
, pa_device_type_t type
, uint32_t sink_idx
) {
149 pa_native_connection
*c
;
152 if (sink_idx
!= PA_INVALID_INDEX
) {
153 PA_IDXSET_FOREACH(c
, u
->subscribed
, idx
) {
156 t
= pa_tagstruct_new(NULL
, 0);
157 pa_tagstruct_putu32(t
, PA_COMMAND_EXTENSION
);
158 pa_tagstruct_putu32(t
, 0);
159 pa_tagstruct_putu32(t
, u
->module
->index
);
160 pa_tagstruct_puts(t
, u
->module
->name
);
161 pa_tagstruct_putu32(t
, SUBCOMMAND_EVENT
);
162 pa_tagstruct_putu32(t
, type
);
163 pa_tagstruct_putu32(t
, sink_idx
);
165 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), t
);
169 if (u
->save_time_event
)
172 u
->save_time_event
= pa_core_rttime_new(u
->core
, pa_rtclock_now() + SAVE_INTERVAL
, save_time_callback
, u
);
175 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
176 /* Some forward declarations */
177 static pa_bool_t
legacy_entry_read(struct userdata
*u
, pa_datum
*data
, struct entry
**entry
, struct perportentry
**perportentry
);
178 static struct perportentry
* perportentry_read(struct userdata
*u
, const char *basekeyname
, const char *port
);
179 static pa_bool_t
perportentry_write(struct userdata
*u
, const char *basekeyname
, const char *port
, const struct perportentry
*e
);
180 static void perportentry_free(struct perportentry
* e
);
183 static struct entry
* entry_new(void) {
184 struct entry
*r
= pa_xnew0(struct entry
, 1);
185 r
->version
= ENTRY_VERSION
;
189 static void entry_free(struct entry
* e
) {
196 static pa_bool_t
entry_write(struct userdata
*u
, const char *name
, const struct entry
*e
) {
205 t
= pa_tagstruct_new(NULL
, 0);
206 pa_tagstruct_putu8(t
, e
->version
);
207 pa_tagstruct_put_boolean(t
, e
->port_valid
);
208 pa_tagstruct_puts(t
, e
->port
);
210 key
.data
= (char *) name
;
211 key
.size
= strlen(name
);
213 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
215 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
217 pa_tagstruct_free(t
);
222 static struct entry
* entry_read(struct userdata
*u
, const char *name
) {
224 struct entry
*e
= NULL
;
225 pa_tagstruct
*t
= NULL
;
231 key
.data
= (char*) name
;
232 key
.size
= strlen(name
);
236 if (!pa_database_get(u
->database
, &key
, &data
))
239 t
= pa_tagstruct_new(data
.data
, data
.size
);
242 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
243 e
->version
> ENTRY_VERSION
||
244 pa_tagstruct_get_boolean(t
, &e
->port_valid
) < 0 ||
245 pa_tagstruct_gets(t
, &port
) < 0) {
250 if (!pa_tagstruct_eof(t
))
253 e
->port
= pa_xstrdup(port
);
255 pa_tagstruct_free(t
);
256 pa_datum_free(&data
);
262 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name
);
267 pa_tagstruct_free(t
);
269 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
271 struct perportentry
*ppe
;
272 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name
);
273 if (legacy_entry_read(u
, &data
, &e
, &ppe
)) {
274 pa_bool_t written
= FALSE
;
276 pa_log_debug("Success. Saving new format for key: %s", name
);
277 written
= entry_write(u
, name
, e
);
279 /* Now convert the legacy entry into per-port entries */
280 if (0 == strncmp("sink:", name
, 5)) {
283 if ((sink
= pa_namereg_get(u
->core
, name
+5, PA_NAMEREG_SINK
))) {
284 /* Write a "null" port entry. The read code will automatically try this
285 * if it cannot find a specific port-named entry. */
286 written
= perportentry_write(u
, name
, NULL
, ppe
) || written
;
288 } else if (0 == strncmp("source:", name
, 7)) {
291 if ((source
= pa_namereg_get(u
->core
, name
+7, PA_NAMEREG_SOURCE
))) {
292 /* Write a "null" port entry. The read code will automatically try this
293 * if it cannot find a specific port-named entry. */
294 written
= perportentry_write(u
, name
, NULL
, ppe
) || written
;
297 perportentry_free(ppe
);
300 /* NB The device type doesn't matter when we pass in an invalid index. */
301 trigger_save(u
, PA_DEVICE_TYPE_SINK
, PA_INVALID_INDEX
);
303 pa_datum_free(&data
);
306 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name
);
310 pa_datum_free(&data
);
314 static struct entry
* entry_copy(const struct entry
*e
) {
319 r
->version
= e
->version
;
320 r
->port_valid
= e
->port_valid
;
321 r
->port
= pa_xstrdup(e
->port
);
326 static pa_bool_t
entries_equal(const struct entry
*a
, const struct entry
*b
) {
330 if (a
->port_valid
!= b
->port_valid
||
331 (a
->port_valid
&& !pa_streq(a
->port
, b
->port
)))
337 static struct perportentry
* perportentry_new(pa_bool_t add_pcm_format
) {
338 struct perportentry
*r
= pa_xnew0(struct perportentry
, 1);
339 r
->version
= PERPORTENTRY_VERSION
;
340 r
->formats
= pa_idxset_new(NULL
, NULL
);
341 if (add_pcm_format
) {
342 pa_format_info
*f
= pa_format_info_new();
343 f
->encoding
= PA_ENCODING_PCM
;
344 pa_idxset_put(r
->formats
, f
, NULL
);
349 static void perportentry_free(struct perportentry
* e
) {
352 pa_idxset_free(e
->formats
, (pa_free_cb_t
) pa_format_info_free
);
356 static pa_bool_t
perportentry_write(struct userdata
*u
, const char *basekeyname
, const char *port
, const struct perportentry
*e
) {
366 pa_assert(basekeyname
);
369 name
= pa_sprintf_malloc("%s:%s", basekeyname
, (port
? port
: "null"));
371 n_formats
= pa_idxset_size(e
->formats
);
372 pa_assert(n_formats
> 0);
374 t
= pa_tagstruct_new(NULL
, 0);
375 pa_tagstruct_putu8(t
, e
->version
);
376 pa_tagstruct_put_boolean(t
, e
->volume_valid
);
377 pa_tagstruct_put_channel_map(t
, &e
->channel_map
);
378 pa_tagstruct_put_cvolume(t
, &e
->volume
);
379 pa_tagstruct_put_boolean(t
, e
->muted_valid
);
380 pa_tagstruct_put_boolean(t
, e
->muted
);
381 pa_tagstruct_putu8(t
, n_formats
);
383 PA_IDXSET_FOREACH(f
, e
->formats
, i
) {
384 pa_tagstruct_put_format_info(t
, f
);
387 key
.data
= (char *) name
;
388 key
.size
= strlen(name
);
390 data
.data
= (void*)pa_tagstruct_data(t
, &data
.size
);
392 r
= (pa_database_set(u
->database
, &key
, &data
, TRUE
) == 0);
394 pa_tagstruct_free(t
);
400 static struct perportentry
* perportentry_read(struct userdata
*u
, const char *basekeyname
, const char *port
) {
402 struct perportentry
*e
= NULL
;
403 pa_tagstruct
*t
= NULL
;
404 uint8_t i
, n_formats
;
408 pa_assert(basekeyname
);
410 name
= pa_sprintf_malloc("%s:%s", basekeyname
, (port
? port
: "null"));
413 key
.size
= strlen(name
);
417 if (!pa_database_get(u
->database
, &key
, &data
))
420 t
= pa_tagstruct_new(data
.data
, data
.size
);
421 e
= perportentry_new(FALSE
);
423 if (pa_tagstruct_getu8(t
, &e
->version
) < 0 ||
424 e
->version
> PERPORTENTRY_VERSION
||
425 pa_tagstruct_get_boolean(t
, &e
->volume_valid
) < 0 ||
426 pa_tagstruct_get_channel_map(t
, &e
->channel_map
) < 0 ||
427 pa_tagstruct_get_cvolume(t
, &e
->volume
) < 0 ||
428 pa_tagstruct_get_boolean(t
, &e
->muted_valid
) < 0 ||
429 pa_tagstruct_get_boolean(t
, &e
->muted
) < 0 ||
430 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
435 for (i
= 0; i
< n_formats
; ++i
) {
436 pa_format_info
*f
= pa_format_info_new();
437 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
438 pa_format_info_free(f
);
441 pa_idxset_put(e
->formats
, f
, NULL
);
444 if (!pa_tagstruct_eof(t
))
447 if (e
->volume_valid
&& !pa_channel_map_valid(&e
->channel_map
)) {
448 pa_log_warn("Invalid channel map stored in database for device %s", name
);
452 if (e
->volume_valid
&& (!pa_cvolume_valid(&e
->volume
) || !pa_cvolume_compatible_with_channel_map(&e
->volume
, &e
->channel_map
))) {
453 pa_log_warn("Volume and channel map don't match in database entry for device %s", name
);
457 pa_tagstruct_free(t
);
458 pa_datum_free(&data
);
466 perportentry_free(e
);
468 pa_tagstruct_free(t
);
470 pa_datum_free(&data
);
472 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
473 /* Try again with a null port. This is used when dealing with migration from older versions */
476 return perportentry_read(u
, basekeyname
, NULL
);
480 pa_log_debug("Database contains invalid data for key: %s", name
);
487 static struct perportentry
* perportentry_copy(const struct perportentry
*e
) {
488 struct perportentry
* r
;
493 r
= perportentry_new(FALSE
);
494 r
->version
= e
->version
;
495 r
->muted_valid
= e
->muted_valid
;
496 r
->volume_valid
= e
->volume_valid
;
498 r
->channel_map
= e
->channel_map
;
499 r
->volume
= e
->volume
;
501 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
502 pa_idxset_put(r
->formats
, pa_format_info_copy(f
), NULL
);
507 static pa_bool_t
perportentries_equal(const struct perportentry
*a
, const struct perportentry
*b
) {
512 if (a
->muted_valid
!= b
->muted_valid
||
513 (a
->muted_valid
&& (a
->muted
!= b
->muted
)))
517 if (a
->volume_valid
!= b
->volume_valid
||
518 (a
->volume_valid
&& !pa_cvolume_equal(pa_cvolume_remap(&t
, &b
->channel_map
, &a
->channel_map
), &a
->volume
)))
521 if (pa_idxset_size(a
->formats
) != pa_idxset_size(b
->formats
))
524 /** TODO: Compare a bit better */
529 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
531 #define LEGACY_ENTRY_VERSION 2
532 static pa_bool_t
legacy_entry_read(struct userdata
*u
, pa_datum
*data
, struct entry
**entry
, struct perportentry
**perportentry
) {
533 struct legacy_entry
{
535 pa_bool_t muted_valid
:1, volume_valid
:1, port_valid
:1;
537 pa_channel_map channel_map
;
539 char port
[PA_NAME_MAX
];
541 struct legacy_entry
*le
;
546 pa_assert(perportentry
);
548 if (data
->size
!= sizeof(struct legacy_entry
)) {
549 pa_log_debug("Size does not match.");
553 le
= (struct legacy_entry
*)data
->data
;
555 if (le
->version
!= LEGACY_ENTRY_VERSION
) {
556 pa_log_debug("Version mismatch.");
560 if (!memchr(le
->port
, 0, sizeof(le
->port
))) {
561 pa_log_warn("Port has missing NUL byte.");
565 if (le
->volume_valid
&& !pa_channel_map_valid(&le
->channel_map
)) {
566 pa_log_warn("Invalid channel map.");
570 if (le
->volume_valid
&& (!pa_cvolume_valid(&le
->volume
) || !pa_cvolume_compatible_with_channel_map(&le
->volume
, &le
->channel_map
))) {
571 pa_log_warn("Volume and channel map don't match.");
575 *entry
= entry_new();
576 (*entry
)->port_valid
= le
->port_valid
;
577 (*entry
)->port
= pa_xstrdup(le
->port
);
579 *perportentry
= perportentry_new(TRUE
);
580 (*perportentry
)->muted_valid
= le
->muted_valid
;
581 (*perportentry
)->volume_valid
= le
->volume_valid
;
582 (*perportentry
)->muted
= le
->muted
;
583 (*perportentry
)->channel_map
= le
->channel_map
;
584 (*perportentry
)->volume
= le
->volume
;
590 static void subscribe_callback(pa_core
*c
, pa_subscription_event_type_t t
, uint32_t idx
, void *userdata
) {
591 struct userdata
*u
= userdata
;
592 struct entry
*e
, *olde
;
593 struct perportentry
*ppe
, *oldppe
;
595 const char *port
= NULL
;
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
;
615 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
616 if (sink
->active_port
)
617 port
= sink
->active_port
->name
;
619 if ((olde
= entry_read(u
, name
)))
620 e
= entry_copy(olde
);
624 if (sink
->save_port
) {
626 e
->port
= pa_xstrdup(port
? port
: "");
627 e
->port_valid
= TRUE
;
630 if ((oldppe
= perportentry_read(u
, name
, port
)))
631 ppe
= perportentry_copy(oldppe
);
633 ppe
= perportentry_new(TRUE
);
635 if (sink
->save_volume
) {
636 ppe
->channel_map
= sink
->channel_map
;
637 ppe
->volume
= *pa_sink_get_volume(sink
, FALSE
);
638 ppe
->volume_valid
= TRUE
;
641 if (sink
->save_muted
) {
642 ppe
->muted
= pa_sink_get_mute(sink
, FALSE
);
643 ppe
->muted_valid
= TRUE
;
648 pa_assert((t
& PA_SUBSCRIPTION_EVENT_FACILITY_MASK
) == PA_SUBSCRIPTION_EVENT_SOURCE
);
650 if (!(source
= pa_idxset_get_by_index(c
->sources
, idx
)))
653 type
= PA_DEVICE_TYPE_SOURCE
;
654 name
= pa_sprintf_malloc("source:%s", source
->name
);
655 if (source
->active_port
)
656 port
= source
->active_port
->name
;
658 if ((olde
= entry_read(u
, name
)))
659 e
= entry_copy(olde
);
663 if (source
->save_port
) {
665 e
->port
= pa_xstrdup(port
? port
: "");
666 e
->port_valid
= TRUE
;
669 if ((oldppe
= perportentry_read(u
, name
, port
)))
670 ppe
= perportentry_copy(oldppe
);
672 ppe
= perportentry_new(TRUE
);
674 if (source
->save_volume
) {
675 ppe
->channel_map
= source
->channel_map
;
676 ppe
->volume
= *pa_source_get_volume(source
, FALSE
);
677 ppe
->volume_valid
= TRUE
;
680 if (source
->save_muted
) {
681 ppe
->muted
= pa_source_get_mute(source
, FALSE
);
682 ppe
->muted_valid
= TRUE
;
690 if (entries_equal(olde
, e
)) {
699 pa_log_info("Storing port for device %s.", name
);
701 written
= entry_write(u
, name
, e
);
710 if (perportentries_equal(oldppe
, ppe
)) {
711 perportentry_free(oldppe
);
712 perportentry_free(ppe
);
715 perportentry_free(oldppe
);
719 pa_log_info("Storing volume/mute for device+port %s:%s.", name
, (port
? port
: "null"));
721 written
= perportentry_write(u
, name
, port
, ppe
) || written
;
723 perportentry_free(ppe
);
728 trigger_save(u
, type
, idx
);
731 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
738 pa_assert(u
->restore_port
);
740 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
742 if ((e
= entry_read(u
, name
))) {
745 if (!new_data
->active_port
) {
746 pa_log_info("Restoring port for sink %s.", name
);
747 pa_sink_new_data_set_port(new_data
, e
->port
);
748 new_data
->save_port
= TRUE
;
750 pa_log_debug("Not restoring port for sink %s, because already set.", name
);
761 static pa_hook_result_t
sink_fixate_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
763 struct perportentry
*e
;
768 pa_assert(u
->restore_volume
|| u
->restore_muted
);
770 name
= pa_sprintf_malloc("sink:%s", new_data
->name
);
772 if ((e
= perportentry_read(u
, name
, new_data
->active_port
))) {
774 if (u
->restore_volume
&& e
->volume_valid
) {
776 if (!new_data
->volume_is_set
) {
778 char buf
[PA_CVOLUME_SNPRINT_MAX
];
780 pa_log_info("Restoring volume for sink %s.", new_data
->name
);
782 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
783 pa_sink_new_data_set_volume(new_data
, &v
);
784 pa_log_info("Restored volume: %s", pa_cvolume_snprint(buf
, PA_CVOLUME_SNPRINT_MAX
, &new_data
->volume
));
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", sink
->name
);
820 if ((e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
)))) {
822 if (u
->restore_volume
&& e
->volume_valid
) {
824 char buf
[PA_CVOLUME_SNPRINT_MAX
];
826 pa_log_info("Restoring volume for sink %s.", sink
->name
);
828 pa_cvolume_remap(&v
, &e
->channel_map
, &sink
->channel_map
);
829 pa_sink_set_volume(sink
, &v
, TRUE
, FALSE
);
830 pa_log_info("Restored volume: %s", pa_cvolume_snprint(buf
, PA_CVOLUME_SNPRINT_MAX
, &sink
->reference_volume
));
832 sink
->save_volume
= TRUE
;
835 if (u
->restore_muted
&& e
->muted_valid
) {
837 pa_log_info("Restoring mute state for sink %s.", sink
->name
);
838 pa_sink_set_mute(sink
, e
->muted
, FALSE
);
839 sink
->save_muted
= TRUE
;
842 perportentry_free(e
);
850 static pa_hook_result_t
sink_put_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
852 struct perportentry
*e
;
857 pa_assert(u
->restore_formats
);
859 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
861 if ((e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
)))) {
863 if (!pa_sink_set_formats(sink
, e
->formats
))
864 pa_log_debug("Could not set format on sink %s", sink
->name
);
866 perportentry_free(e
);
874 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
881 pa_assert(u
->restore_port
);
883 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
885 if ((e
= entry_read(u
, name
))) {
888 if (!new_data
->active_port
) {
889 pa_log_info("Restoring port for source %s.", name
);
890 pa_source_new_data_set_port(new_data
, e
->port
);
891 new_data
->save_port
= TRUE
;
893 pa_log_debug("Not restoring port for source %s, because already set.", name
);
904 static pa_hook_result_t
source_fixate_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
906 struct perportentry
*e
;
911 pa_assert(u
->restore_volume
|| u
->restore_muted
);
913 name
= pa_sprintf_malloc("source:%s", new_data
->name
);
915 if ((e
= perportentry_read(u
, name
, new_data
->active_port
))) {
917 if (u
->restore_volume
&& e
->volume_valid
) {
919 if (!new_data
->volume_is_set
) {
921 char buf
[PA_CVOLUME_SNPRINT_MAX
];
923 pa_log_info("Restoring volume for source %s.", new_data
->name
);
925 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
926 pa_source_new_data_set_volume(new_data
, &v
);
927 pa_log_info("Restored volume: %s", pa_cvolume_snprint(buf
, PA_CVOLUME_SNPRINT_MAX
, &new_data
->volume
));
929 new_data
->save_volume
= TRUE
;
931 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
934 if (u
->restore_muted
&& e
->muted_valid
) {
936 if (!new_data
->muted_is_set
) {
937 pa_log_info("Restoring mute state for source %s.", new_data
->name
);
938 pa_source_new_data_set_muted(new_data
, e
->muted
);
939 new_data
->save_muted
= TRUE
;
941 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data
->name
);
944 perportentry_free(e
);
952 static pa_hook_result_t
source_port_hook_callback(pa_core
*c
, pa_source
*source
, struct userdata
*u
) {
954 struct perportentry
*e
;
959 pa_assert(u
->restore_volume
|| u
->restore_muted
);
961 name
= pa_sprintf_malloc("source:%s", source
->name
);
963 if ((e
= perportentry_read(u
, name
, (source
->active_port
? source
->active_port
->name
: NULL
)))) {
965 if (u
->restore_volume
&& e
->volume_valid
) {
967 char buf
[PA_CVOLUME_SNPRINT_MAX
];
969 pa_log_info("Restoring volume for source %s.", source
->name
);
971 pa_cvolume_remap(&v
, &e
->channel_map
, &source
->channel_map
);
972 pa_source_set_volume(source
, &v
, TRUE
, FALSE
);
973 pa_log_info("Restored volume: %s", pa_cvolume_snprint(buf
, PA_CVOLUME_SNPRINT_MAX
, &source
->reference_volume
));
975 source
->save_volume
= TRUE
;
978 if (u
->restore_muted
&& e
->muted_valid
) {
980 pa_log_info("Restoring mute state for source %s.", source
->name
);
981 pa_source_set_mute(source
, e
->muted
, FALSE
);
982 source
->save_muted
= TRUE
;
985 perportentry_free(e
);
993 #define EXT_VERSION 1
995 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
996 struct perportentry
*e
;
1003 pa_tagstruct_putu32(reply
, PA_DEVICE_TYPE_SINK
);
1004 pa_tagstruct_putu32(reply
, sink
->index
);
1006 /* Read or create an entry */
1007 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
1008 if (!(e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
)))) {
1009 /* Fake a reply with PCM encoding supported */
1010 pa_format_info
*f
= pa_format_info_new();
1012 pa_tagstruct_putu8(reply
, 1);
1013 f
->encoding
= PA_ENCODING_PCM
;
1014 pa_tagstruct_put_format_info(reply
, f
);
1016 pa_format_info_free(f
);
1021 /* Write all the formats from the entry to the reply */
1022 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
1023 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
1024 pa_tagstruct_put_format_info(reply
, f
);
1030 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
1033 pa_tagstruct
*reply
= NULL
;
1042 if (pa_tagstruct_getu32(t
, &command
) < 0)
1045 reply
= pa_tagstruct_new(NULL
, 0);
1046 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
1047 pa_tagstruct_putu32(reply
, tag
);
1050 case SUBCOMMAND_TEST
: {
1051 if (!pa_tagstruct_eof(t
))
1054 pa_tagstruct_putu32(reply
, EXT_VERSION
);
1058 case SUBCOMMAND_SUBSCRIBE
: {
1062 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1063 !pa_tagstruct_eof(t
))
1067 pa_idxset_put(u
->subscribed
, c
, NULL
);
1069 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1074 case SUBCOMMAND_READ_FORMATS_ALL
: {
1078 if (!pa_tagstruct_eof(t
))
1081 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
1082 read_sink_format_reply(u
, reply
, sink
);
1087 case SUBCOMMAND_READ_FORMATS
: {
1088 pa_device_type_t type
;
1089 uint32_t sink_index
;
1094 /* Get the sink index and the number of formats from the tagstruct */
1095 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
1096 pa_tagstruct_getu32(t
, &sink_index
) < 0)
1099 if (type
!= PA_DEVICE_TYPE_SINK
) {
1100 pa_log("Device format reading is only supported on sinks");
1104 if (!pa_tagstruct_eof(t
))
1107 /* Now find our sink */
1108 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
1111 read_sink_format_reply(u
, reply
, sink
);
1116 case SUBCOMMAND_SAVE_FORMATS
: {
1118 struct perportentry
*e
;
1119 pa_device_type_t type
;
1120 uint32_t sink_index
;
1123 uint8_t i
, n_formats
;
1125 /* Get the sink index and the number of formats from the tagstruct */
1126 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
1127 pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
1128 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
1133 if (type
!= PA_DEVICE_TYPE_SINK
) {
1134 pa_log("Device format saving is only supported on sinks");
1138 /* Now find our sink */
1139 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
))) {
1140 pa_log("Could not find sink #%d", sink_index
);
1144 /* Read or create an entry */
1145 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
1146 if (!(e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
))))
1147 e
= perportentry_new(FALSE
);
1149 /* Clean out any saved formats */
1150 pa_idxset_free(e
->formats
, (pa_free_cb_t
) pa_format_info_free
);
1151 e
->formats
= pa_idxset_new(NULL
, NULL
);
1154 /* Read all the formats from our tagstruct */
1155 for (i
= 0; i
< n_formats
; ++i
) {
1156 pa_format_info
*f
= pa_format_info_new();
1157 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
1158 pa_format_info_free(f
);
1162 pa_idxset_put(e
->formats
, f
, NULL
);
1165 if (!pa_tagstruct_eof(t
)) {
1166 perportentry_free(e
);
1171 if (pa_sink_set_formats(sink
, e
->formats
) && perportentry_write(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
), e
))
1172 trigger_save(u
, type
, sink_index
);
1174 pa_log_warn("Could not save format info for sink %s", sink
->name
);
1177 perportentry_free(e
);
1186 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1192 pa_tagstruct_free(reply
);
1197 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1202 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1206 int pa__init(pa_module
*m
) {
1207 pa_modargs
*ma
= NULL
;
1213 pa_bool_t restore_volume
= TRUE
, restore_muted
= TRUE
, restore_port
= TRUE
, restore_formats
= TRUE
;
1217 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1218 pa_log("Failed to parse module arguments");
1222 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
1223 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
1224 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0 ||
1225 pa_modargs_get_value_boolean(ma
, "restore_formats", &restore_formats
) < 0) {
1226 pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
1230 if (!restore_muted
&& !restore_volume
&& !restore_port
&& !restore_formats
)
1231 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
1233 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1236 u
->restore_volume
= restore_volume
;
1237 u
->restore_muted
= restore_muted
;
1238 u
->restore_port
= restore_port
;
1239 u
->restore_formats
= restore_formats
;
1241 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1243 u
->protocol
= pa_native_protocol_get(m
->core
);
1244 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1246 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
);
1248 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1251 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
);
1252 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
);
1255 if (restore_muted
|| restore_volume
) {
1256 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
);
1257 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
);
1259 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
);
1260 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
);
1263 if (restore_formats
)
1264 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
);
1266 if (!(fname
= pa_state_path("device-volumes", TRUE
)))
1269 if (!(u
->database
= pa_database_open(fname
, TRUE
))) {
1270 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1275 pa_log_info("Successfully opened database file '%s'.", fname
);
1278 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1279 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1281 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1282 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1284 pa_modargs_free(ma
);
1291 pa_modargs_free(ma
);
1296 void pa__done(pa_module
*m
) {
1301 if (!(u
= m
->userdata
))
1304 if (u
->subscription
)
1305 pa_subscription_free(u
->subscription
);
1307 if (u
->sink_fixate_hook_slot
)
1308 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
1309 if (u
->source_fixate_hook_slot
)
1310 pa_hook_slot_free(u
->source_fixate_hook_slot
);
1311 if (u
->sink_new_hook_slot
)
1312 pa_hook_slot_free(u
->sink_new_hook_slot
);
1313 if (u
->source_new_hook_slot
)
1314 pa_hook_slot_free(u
->source_new_hook_slot
);
1315 if (u
->sink_port_hook_slot
)
1316 pa_hook_slot_free(u
->sink_port_hook_slot
);
1317 if (u
->source_port_hook_slot
)
1318 pa_hook_slot_free(u
->source_port_hook_slot
);
1319 if (u
->sink_put_hook_slot
)
1320 pa_hook_slot_free(u
->sink_put_hook_slot
);
1322 if (u
->connection_unlink_hook_slot
)
1323 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1325 if (u
->save_time_event
)
1326 u
->core
->mainloop
->time_free(u
->save_time_event
);
1329 pa_database_close(u
->database
);
1332 pa_native_protocol_remove_ext(u
->protocol
, m
);
1333 pa_native_protocol_unref(u
->protocol
);
1337 pa_idxset_free(u
->subscribed
, NULL
);