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 bool restore_volume
:1;
101 bool 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
122 #define PERPORTENTRY_VERSION 1
124 struct perportentry
{
126 bool 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 bool 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 bool 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 bool 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 bool 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 bool 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(bool 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 bool 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 bool 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 bool legacy_entry_read(struct userdata
*u
, pa_datum
*data
, struct entry
**entry
, struct perportentry
**perportentry
) {
533 struct legacy_entry
{
535 bool 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 bool 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_VERBOSE_MAX
];
781 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
782 pa_sink_new_data_set_volume(new_data
, &v
);
783 pa_log_info("Restoring volume for sink %s: %s", new_data
->name
,
784 pa_cvolume_snprint_verbose(buf
, sizeof(buf
), &new_data
->volume
, &new_data
->channel_map
, false));
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_sink_new_data_set_muted(new_data
, e
->muted
);
795 new_data
->save_muted
= true;
796 pa_log_info("Restoring mute state for sink %s: %smuted", new_data
->name
,
797 new_data
->muted
? "" : "un");
799 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data
->name
);
802 perportentry_free(e
);
810 static pa_hook_result_t
sink_port_hook_callback(pa_core
*c
, pa_sink
*sink
, struct userdata
*u
) {
812 struct perportentry
*e
;
817 pa_assert(u
->restore_volume
|| u
->restore_muted
);
819 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
821 if ((e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
)))) {
823 if (u
->restore_volume
&& e
->volume_valid
) {
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);
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", sink
->name
);
860 if ((e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
)))) {
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", new_data
->name
);
914 if ((e
= perportentry_read(u
, name
, new_data
->active_port
))) {
916 if (u
->restore_volume
&& e
->volume_valid
) {
918 if (!new_data
->volume_is_set
) {
920 char buf
[PA_CVOLUME_SNPRINT_VERBOSE_MAX
];
923 pa_cvolume_remap(&v
, &e
->channel_map
, &new_data
->channel_map
);
924 pa_source_new_data_set_volume(new_data
, &v
);
925 pa_log_info("Restoring volume for source %s: %s", new_data
->name
,
926 pa_cvolume_snprint_verbose(buf
, sizeof(buf
), &new_data
->volume
, &new_data
->channel_map
, false));
928 new_data
->save_volume
= true;
930 pa_log_debug("Not restoring volume for source %s, because already set.", new_data
->name
);
933 if (u
->restore_muted
&& e
->muted_valid
) {
935 if (!new_data
->muted_is_set
) {
936 pa_source_new_data_set_muted(new_data
, e
->muted
);
937 new_data
->save_muted
= true;
938 pa_log_info("Restoring mute state for source %s: %smuted", new_data
->name
,
939 new_data
->muted
? "" : "un");
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
) {
968 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);
973 source
->save_volume
= true;
976 if (u
->restore_muted
&& e
->muted_valid
) {
978 pa_log_info("Restoring mute state for source %s.", source
->name
);
979 pa_source_set_mute(source
, e
->muted
, false);
980 source
->save_muted
= true;
983 perportentry_free(e
);
991 #define EXT_VERSION 1
993 static void read_sink_format_reply(struct userdata
*u
, pa_tagstruct
*reply
, pa_sink
*sink
) {
994 struct perportentry
*e
;
1001 pa_tagstruct_putu32(reply
, PA_DEVICE_TYPE_SINK
);
1002 pa_tagstruct_putu32(reply
, sink
->index
);
1004 /* Read or create an entry */
1005 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
1006 if (!(e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
)))) {
1007 /* Fake a reply with PCM encoding supported */
1008 pa_format_info
*f
= pa_format_info_new();
1010 pa_tagstruct_putu8(reply
, 1);
1011 f
->encoding
= PA_ENCODING_PCM
;
1012 pa_tagstruct_put_format_info(reply
, f
);
1014 pa_format_info_free(f
);
1019 /* Write all the formats from the entry to the reply */
1020 pa_tagstruct_putu8(reply
, pa_idxset_size(e
->formats
));
1021 PA_IDXSET_FOREACH(f
, e
->formats
, idx
) {
1022 pa_tagstruct_put_format_info(reply
, f
);
1028 static int extension_cb(pa_native_protocol
*p
, pa_module
*m
, pa_native_connection
*c
, uint32_t tag
, pa_tagstruct
*t
) {
1031 pa_tagstruct
*reply
= NULL
;
1040 if (pa_tagstruct_getu32(t
, &command
) < 0)
1043 reply
= pa_tagstruct_new(NULL
, 0);
1044 pa_tagstruct_putu32(reply
, PA_COMMAND_REPLY
);
1045 pa_tagstruct_putu32(reply
, tag
);
1048 case SUBCOMMAND_TEST
: {
1049 if (!pa_tagstruct_eof(t
))
1052 pa_tagstruct_putu32(reply
, EXT_VERSION
);
1056 case SUBCOMMAND_SUBSCRIBE
: {
1060 if (pa_tagstruct_get_boolean(t
, &enabled
) < 0 ||
1061 !pa_tagstruct_eof(t
))
1065 pa_idxset_put(u
->subscribed
, c
, NULL
);
1067 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1072 case SUBCOMMAND_READ_FORMATS_ALL
: {
1076 if (!pa_tagstruct_eof(t
))
1079 PA_IDXSET_FOREACH(sink
, u
->core
->sinks
, idx
) {
1080 read_sink_format_reply(u
, reply
, sink
);
1085 case SUBCOMMAND_READ_FORMATS
: {
1086 pa_device_type_t type
;
1087 uint32_t sink_index
;
1092 /* Get the sink index and the number of formats from the tagstruct */
1093 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
1094 pa_tagstruct_getu32(t
, &sink_index
) < 0)
1097 if (type
!= PA_DEVICE_TYPE_SINK
) {
1098 pa_log("Device format reading is only supported on sinks");
1102 if (!pa_tagstruct_eof(t
))
1105 /* Now find our sink */
1106 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
)))
1109 read_sink_format_reply(u
, reply
, sink
);
1114 case SUBCOMMAND_SAVE_FORMATS
: {
1116 struct perportentry
*e
;
1117 pa_device_type_t type
;
1118 uint32_t sink_index
;
1121 uint8_t i
, n_formats
;
1123 /* Get the sink index and the number of formats from the tagstruct */
1124 if (pa_tagstruct_getu32(t
, &type
) < 0 ||
1125 pa_tagstruct_getu32(t
, &sink_index
) < 0 ||
1126 pa_tagstruct_getu8(t
, &n_formats
) < 0 || n_formats
< 1) {
1131 if (type
!= PA_DEVICE_TYPE_SINK
) {
1132 pa_log("Device format saving is only supported on sinks");
1136 /* Now find our sink */
1137 if (!(sink
= pa_idxset_get_by_index(u
->core
->sinks
, sink_index
))) {
1138 pa_log("Could not find sink #%d", sink_index
);
1142 /* Read or create an entry */
1143 name
= pa_sprintf_malloc("sink:%s", sink
->name
);
1144 if (!(e
= perportentry_read(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
))))
1145 e
= perportentry_new(false);
1147 /* Clean out any saved formats */
1148 pa_idxset_free(e
->formats
, (pa_free_cb_t
) pa_format_info_free
);
1149 e
->formats
= pa_idxset_new(NULL
, NULL
);
1152 /* Read all the formats from our tagstruct */
1153 for (i
= 0; i
< n_formats
; ++i
) {
1154 pa_format_info
*f
= pa_format_info_new();
1155 if (pa_tagstruct_get_format_info(t
, f
) < 0) {
1156 pa_format_info_free(f
);
1157 perportentry_free(e
);
1161 pa_idxset_put(e
->formats
, f
, NULL
);
1164 if (!pa_tagstruct_eof(t
)) {
1165 perportentry_free(e
);
1170 if (pa_sink_set_formats(sink
, e
->formats
) && perportentry_write(u
, name
, (sink
->active_port
? sink
->active_port
->name
: NULL
), e
))
1171 trigger_save(u
, type
, sink_index
);
1173 pa_log_warn("Could not save format info for sink %s", sink
->name
);
1176 perportentry_free(e
);
1185 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c
), reply
);
1191 pa_tagstruct_free(reply
);
1196 static pa_hook_result_t
connection_unlink_hook_cb(pa_native_protocol
*p
, pa_native_connection
*c
, struct userdata
*u
) {
1201 pa_idxset_remove_by_data(u
->subscribed
, c
, NULL
);
1205 int pa__init(pa_module
*m
) {
1206 pa_modargs
*ma
= NULL
;
1212 bool restore_volume
= true, restore_muted
= true, restore_port
= true, restore_formats
= true;
1216 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
1217 pa_log("Failed to parse module arguments");
1221 if (pa_modargs_get_value_boolean(ma
, "restore_volume", &restore_volume
) < 0 ||
1222 pa_modargs_get_value_boolean(ma
, "restore_muted", &restore_muted
) < 0 ||
1223 pa_modargs_get_value_boolean(ma
, "restore_port", &restore_port
) < 0 ||
1224 pa_modargs_get_value_boolean(ma
, "restore_formats", &restore_formats
) < 0) {
1225 pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
1229 if (!restore_muted
&& !restore_volume
&& !restore_port
&& !restore_formats
)
1230 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
1232 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
1235 u
->restore_volume
= restore_volume
;
1236 u
->restore_muted
= restore_muted
;
1237 u
->restore_port
= restore_port
;
1238 u
->restore_formats
= restore_formats
;
1240 u
->subscribed
= pa_idxset_new(pa_idxset_trivial_hash_func
, pa_idxset_trivial_compare_func
);
1242 u
->protocol
= pa_native_protocol_get(m
->core
);
1243 pa_native_protocol_install_ext(u
->protocol
, m
, extension_cb
);
1245 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
);
1247 u
->subscription
= pa_subscription_new(m
->core
, PA_SUBSCRIPTION_MASK_SINK
|PA_SUBSCRIPTION_MASK_SOURCE
, subscribe_callback
, u
);
1250 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
);
1251 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
);
1254 if (restore_muted
|| restore_volume
) {
1255 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
);
1256 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
);
1258 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
);
1259 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
);
1262 if (restore_formats
)
1263 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
);
1265 if (!(fname
= pa_state_path("device-volumes", true)))
1268 if (!(u
->database
= pa_database_open(fname
, true))) {
1269 pa_log("Failed to open volume database '%s': %s", fname
, pa_cstrerror(errno
));
1274 pa_log_info("Successfully opened database file '%s'.", fname
);
1277 PA_IDXSET_FOREACH(sink
, m
->core
->sinks
, idx
)
1278 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SINK
|PA_SUBSCRIPTION_EVENT_NEW
, sink
->index
, u
);
1280 PA_IDXSET_FOREACH(source
, m
->core
->sources
, idx
)
1281 subscribe_callback(m
->core
, PA_SUBSCRIPTION_EVENT_SOURCE
|PA_SUBSCRIPTION_EVENT_NEW
, source
->index
, u
);
1283 pa_modargs_free(ma
);
1290 pa_modargs_free(ma
);
1295 void pa__done(pa_module
*m
) {
1300 if (!(u
= m
->userdata
))
1303 if (u
->subscription
)
1304 pa_subscription_free(u
->subscription
);
1306 if (u
->sink_fixate_hook_slot
)
1307 pa_hook_slot_free(u
->sink_fixate_hook_slot
);
1308 if (u
->source_fixate_hook_slot
)
1309 pa_hook_slot_free(u
->source_fixate_hook_slot
);
1310 if (u
->sink_new_hook_slot
)
1311 pa_hook_slot_free(u
->sink_new_hook_slot
);
1312 if (u
->source_new_hook_slot
)
1313 pa_hook_slot_free(u
->source_new_hook_slot
);
1314 if (u
->sink_port_hook_slot
)
1315 pa_hook_slot_free(u
->sink_port_hook_slot
);
1316 if (u
->source_port_hook_slot
)
1317 pa_hook_slot_free(u
->source_port_hook_slot
);
1318 if (u
->sink_put_hook_slot
)
1319 pa_hook_slot_free(u
->sink_put_hook_slot
);
1321 if (u
->connection_unlink_hook_slot
)
1322 pa_hook_slot_free(u
->connection_unlink_hook_slot
);
1324 if (u
->save_time_event
)
1325 u
->core
->mainloop
->time_free(u
->save_time_event
);
1328 pa_database_close(u
->database
);
1331 pa_native_protocol_remove_ext(u
->protocol
, m
);
1332 pa_native_protocol_unref(u
->protocol
);
1336 pa_idxset_free(u
->subscribed
, NULL
);