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, see <http://www.gnu.org/licenses/>.
28 #include <sys/types.h>
32 #include <pulse/gccmacro.h>
33 #include <pulse/xmalloc.h>
34 #include <pulse/volume.h>
35 #include <pulse/timeval.h>
36 #include <pulse/rtclock.h>
37 #include <pulse/format.h>
38 #include <pulse/internal.h>
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/module.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/modargs.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/core-subscribe.h>
46 #include <pulsecore/sink.h>
47 #include <pulsecore/source.h>
48 #include <pulsecore/namereg.h>
49 #include <pulsecore/protocol-native.h>
50 #include <pulsecore/pstream.h>
51 #include <pulsecore/pstream-util.h>
52 #include <pulsecore/database.h>
53 #include <pulsecore/tagstruct.h>
55 PA_MODULE_AUTHOR("Lennart Poettering");
56 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
57 PA_MODULE_VERSION(PACKAGE_VERSION);
58 PA_MODULE_LOAD_ONCE(true);
60 "restore_port=<Save/restore port?> "
61 "restore_volume=<Save/restore volumes?> "
62 "restore_muted=<Save/restore muted states?> "
63 "restore_formats=<Save/restore saved formats?>");
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 static const char* const valid_modargs[] = {
78 pa_subscription *subscription;
79 pa_time_event *save_time_event;
80 pa_database *database;
82 pa_native_protocol *protocol;
83 pa_idxset *subscribed;
85 bool restore_volume:1;
88 bool restore_formats:1;
91 /* Protocol extension commands */
96 SUBCOMMAND_READ_FORMATS_ALL,
97 SUBCOMMAND_READ_FORMATS,
98 SUBCOMMAND_SAVE_FORMATS
101 #define ENTRY_VERSION 1
109 #define PERPORTENTRY_VERSION 1
111 struct perportentry {
113 bool muted_valid, volume_valid;
115 pa_channel_map channel_map;
120 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
121 struct userdata *u = userdata;
127 pa_assert(e == u->save_time_event);
128 u->core->mainloop->time_free(u->save_time_event);
129 u->save_time_event = NULL;
131 pa_database_sync(u->database);
132 pa_log_info("Synced.");
135 static void trigger_save(struct userdata *u, pa_device_type_t type, uint32_t sink_idx) {
136 pa_native_connection *c;
139 if (sink_idx != PA_INVALID_INDEX) {
140 PA_IDXSET_FOREACH(c, u->subscribed, idx) {
143 t = pa_tagstruct_new();
144 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
145 pa_tagstruct_putu32(t, 0);
146 pa_tagstruct_putu32(t, u->module->index);
147 pa_tagstruct_puts(t, u->module->name);
148 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
149 pa_tagstruct_putu32(t, type);
150 pa_tagstruct_putu32(t, sink_idx);
152 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
156 if (u->save_time_event)
159 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
162 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
163 /* Some forward declarations */
164 static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry);
165 static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port);
166 static bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e);
167 static void perportentry_free(struct perportentry* e);
170 static struct entry* entry_new(void) {
171 struct entry *r = pa_xnew0(struct entry, 1);
172 r->version = ENTRY_VERSION;
176 static void entry_free(struct entry* e) {
183 static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
192 t = pa_tagstruct_new();
193 pa_tagstruct_putu8(t, e->version);
194 pa_tagstruct_put_boolean(t, e->port_valid);
195 pa_tagstruct_puts(t, e->port);
197 key.data = (char *) name;
198 key.size = strlen(name);
200 data.data = (void*)pa_tagstruct_data(t, &data.size);
202 r = (pa_database_set(u->database, &key, &data, true) == 0);
204 pa_tagstruct_free(t);
209 static struct entry* entry_read(struct userdata *u, const char *name) {
211 struct entry *e = NULL;
212 pa_tagstruct *t = NULL;
218 key.data = (char*) name;
219 key.size = strlen(name);
223 if (!pa_database_get(u->database, &key, &data)) {
224 pa_log_debug("Database contains no data for key: %s", name);
228 t = pa_tagstruct_new_fixed(data.data, data.size);
231 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
232 e->version > ENTRY_VERSION ||
233 pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
234 pa_tagstruct_gets(t, &port) < 0) {
239 if (!pa_tagstruct_eof(t))
242 e->port = pa_xstrdup(port);
244 pa_tagstruct_free(t);
245 pa_datum_free(&data);
251 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
256 pa_tagstruct_free(t);
258 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
260 struct perportentry *ppe;
261 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
262 if (legacy_entry_read(u, &data, &e, &ppe)) {
263 bool written = false;
265 pa_log_debug("Success. Saving new format for key: %s", name);
266 written = entry_write(u, name, e);
268 /* Now convert the legacy entry into per-port entries */
269 if (0 == strncmp("sink:", name, 5)) {
272 if ((sink = pa_namereg_get(u->core, name+5, PA_NAMEREG_SINK))) {
273 /* Write a "null" port entry. The read code will automatically try this
274 * if it cannot find a specific port-named entry. */
275 written = perportentry_write(u, name, NULL, ppe) || written;
277 } else if (0 == strncmp("source:", name, 7)) {
280 if ((source = pa_namereg_get(u->core, name+7, PA_NAMEREG_SOURCE))) {
281 /* Write a "null" port entry. The read code will automatically try this
282 * if it cannot find a specific port-named entry. */
283 written = perportentry_write(u, name, NULL, ppe) || written;
286 perportentry_free(ppe);
289 /* NB The device type doesn't matter when we pass in an invalid index. */
290 trigger_save(u, PA_DEVICE_TYPE_SINK, PA_INVALID_INDEX);
292 pa_datum_free(&data);
295 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
299 pa_datum_free(&data);
303 static struct entry* entry_copy(const struct entry *e) {
308 r->version = e->version;
309 r->port_valid = e->port_valid;
310 r->port = pa_xstrdup(e->port);
315 static bool entries_equal(const struct entry *a, const struct entry *b) {
319 if (a->port_valid != b->port_valid ||
320 (a->port_valid && !pa_streq(a->port, b->port)))
326 static struct perportentry* perportentry_new(bool add_pcm_format) {
327 struct perportentry *r = pa_xnew0(struct perportentry, 1);
328 r->version = PERPORTENTRY_VERSION;
329 r->formats = pa_idxset_new(NULL, NULL);
330 if (add_pcm_format) {
331 pa_format_info *f = pa_format_info_new();
332 f->encoding = PA_ENCODING_PCM;
333 pa_idxset_put(r->formats, f, NULL);
338 static void perportentry_free(struct perportentry* e) {
341 pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free);
345 static bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e) {
355 pa_assert(basekeyname);
358 name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
360 n_formats = pa_idxset_size(e->formats);
361 pa_assert(n_formats > 0);
363 t = pa_tagstruct_new();
364 pa_tagstruct_putu8(t, e->version);
365 pa_tagstruct_put_boolean(t, e->volume_valid);
366 pa_tagstruct_put_channel_map(t, &e->channel_map);
367 pa_tagstruct_put_cvolume(t, &e->volume);
368 pa_tagstruct_put_boolean(t, e->muted_valid);
369 pa_tagstruct_put_boolean(t, e->muted);
370 pa_tagstruct_putu8(t, n_formats);
372 PA_IDXSET_FOREACH(f, e->formats, i) {
373 pa_tagstruct_put_format_info(t, f);
376 key.data = (char *) name;
377 key.size = strlen(name);
379 data.data = (void*)pa_tagstruct_data(t, &data.size);
381 r = (pa_database_set(u->database, &key, &data, true) == 0);
383 pa_tagstruct_free(t);
389 static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port) {
391 struct perportentry *e = NULL;
392 pa_tagstruct *t = NULL;
393 uint8_t i, n_formats;
397 pa_assert(basekeyname);
399 name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
402 key.size = strlen(name);
406 if (!pa_database_get(u->database, &key, &data))
409 t = pa_tagstruct_new_fixed(data.data, data.size);
410 e = perportentry_new(false);
412 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
413 e->version > PERPORTENTRY_VERSION ||
414 pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
415 pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
416 pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
417 pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
418 pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
419 pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
424 for (i = 0; i < n_formats; ++i) {
425 pa_format_info *f = pa_format_info_new();
426 if (pa_tagstruct_get_format_info(t, f) < 0) {
427 pa_format_info_free(f);
430 pa_idxset_put(e->formats, f, NULL);
433 if (!pa_tagstruct_eof(t))
436 if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
437 pa_log_warn("Invalid channel map stored in database for device %s", name);
441 if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
442 pa_log_warn("Volume and channel map don't match in database entry for device %s", name);
446 pa_tagstruct_free(t);
447 pa_datum_free(&data);
455 perportentry_free(e);
457 pa_tagstruct_free(t);
459 pa_datum_free(&data);
461 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
462 /* Try again with a null port. This is used when dealing with migration from older versions */
465 return perportentry_read(u, basekeyname, NULL);
469 pa_log_debug("Database contains no (or invalid) data for key: %s", name);
476 static struct perportentry* perportentry_copy(const struct perportentry *e) {
477 struct perportentry* r;
482 r = perportentry_new(false);
483 r->version = e->version;
484 r->muted_valid = e->muted_valid;
485 r->volume_valid = e->volume_valid;
487 r->channel_map = e->channel_map;
488 r->volume = e->volume;
490 PA_IDXSET_FOREACH(f, e->formats, idx) {
491 pa_idxset_put(r->formats, pa_format_info_copy(f), NULL);
496 static bool perportentries_equal(const struct perportentry *a, const struct perportentry *b) {
501 if (a->muted_valid != b->muted_valid ||
502 (a->muted_valid && (a->muted != b->muted)))
506 if (a->volume_valid != b->volume_valid ||
507 (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
510 if (pa_idxset_size(a->formats) != pa_idxset_size(b->formats))
513 /** TODO: Compare a bit better */
518 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
520 #define LEGACY_ENTRY_VERSION 2
521 static bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry) {
522 struct legacy_entry {
524 bool muted_valid:1, volume_valid:1, port_valid:1;
526 pa_channel_map channel_map;
528 char port[PA_NAME_MAX];
530 struct legacy_entry *le;
531 pa_channel_map channel_map;
537 pa_assert(perportentry);
539 if (data->size != sizeof(struct legacy_entry)) {
540 pa_log_debug("Size does not match.");
544 le = (struct legacy_entry*)data->data;
546 if (le->version != LEGACY_ENTRY_VERSION) {
547 pa_log_debug("Version mismatch.");
551 if (!memchr(le->port, 0, sizeof(le->port))) {
552 pa_log_warn("Port has missing NUL byte.");
556 /* Read these out before accessing contents via pointers as struct legacy_entry may not be adequately aligned for these
557 * members to be accessed directly */
558 channel_map = le->channel_map;
561 if (le->volume_valid && !pa_channel_map_valid(&channel_map)) {
562 pa_log_warn("Invalid channel map.");
566 if (le->volume_valid && (!pa_cvolume_valid(&volume) || !pa_cvolume_compatible_with_channel_map(&volume, &channel_map))) {
567 pa_log_warn("Volume and channel map don't match.");
571 *entry = entry_new();
572 (*entry)->port_valid = le->port_valid;
573 (*entry)->port = pa_xstrdup(le->port);
575 *perportentry = perportentry_new(true);
576 (*perportentry)->muted_valid = le->muted_valid;
577 (*perportentry)->volume_valid = le->volume_valid;
578 (*perportentry)->muted = le->muted;
579 (*perportentry)->channel_map = le->channel_map;
580 (*perportentry)->volume = le->volume;
586 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
587 struct userdata *u = userdata;
588 struct entry *e, *olde;
589 struct perportentry *ppe, *oldppe;
591 const char *port = NULL;
592 pa_device_type_t type;
593 bool written = false;
598 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
599 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
600 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
601 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
604 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
607 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
610 type = PA_DEVICE_TYPE_SINK;
611 name = pa_sprintf_malloc("sink:%s", sink->name);
612 if (sink->active_port)
613 port = sink->active_port->name;
615 if ((olde = entry_read(u, name)))
616 e = entry_copy(olde);
620 if (sink->save_port) {
622 e->port = pa_xstrdup(port ? port : "");
623 e->port_valid = true;
626 if ((oldppe = perportentry_read(u, name, port)))
627 ppe = perportentry_copy(oldppe);
629 ppe = perportentry_new(true);
631 if (sink->save_volume) {
632 ppe->channel_map = sink->channel_map;
633 ppe->volume = *pa_sink_get_volume(sink, false);
634 ppe->volume_valid = true;
637 if (sink->save_muted) {
638 ppe->muted = pa_sink_get_mute(sink, false);
639 ppe->muted_valid = true;
644 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
646 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
649 type = PA_DEVICE_TYPE_SOURCE;
650 name = pa_sprintf_malloc("source:%s", source->name);
651 if (source->active_port)
652 port = source->active_port->name;
654 if ((olde = entry_read(u, name)))
655 e = entry_copy(olde);
659 if (source->save_port) {
661 e->port = pa_xstrdup(port ? port : "");
662 e->port_valid = true;
665 if ((oldppe = perportentry_read(u, name, port)))
666 ppe = perportentry_copy(oldppe);
668 ppe = perportentry_new(true);
670 if (source->save_volume) {
671 ppe->channel_map = source->channel_map;
672 ppe->volume = *pa_source_get_volume(source, false);
673 ppe->volume_valid = true;
676 if (source->save_muted) {
677 ppe->muted = pa_source_get_mute(source, false);
678 ppe->muted_valid = true;
686 if (entries_equal(olde, e)) {
695 pa_log_info("Storing port for device %s.", name);
697 written = entry_write(u, name, e);
706 if (perportentries_equal(oldppe, ppe)) {
707 perportentry_free(oldppe);
708 perportentry_free(ppe);
711 perportentry_free(oldppe);
715 pa_log_info("Storing volume/mute for device+port %s:%s.", name, (port ? port : "null"));
717 written = perportentry_write(u, name, port, ppe) || written;
719 perportentry_free(ppe);
724 trigger_save(u, type, idx);
727 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
734 pa_assert(u->restore_port);
736 name = pa_sprintf_malloc("sink:%s", new_data->name);
738 if ((e = entry_read(u, name))) {
741 if (!new_data->active_port) {
742 pa_log_info("Restoring port for sink %s.", name);
743 pa_sink_new_data_set_port(new_data, e->port);
744 new_data->save_port = true;
746 pa_log_debug("Not restoring port for sink %s, because already set.", name);
757 static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
759 struct perportentry *e;
764 pa_assert(u->restore_volume || u->restore_muted);
766 name = pa_sprintf_malloc("sink:%s", new_data->name);
768 if ((e = perportentry_read(u, name, new_data->active_port))) {
770 if (u->restore_volume && e->volume_valid) {
772 if (!new_data->volume_is_set) {
774 char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
777 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
778 pa_sink_new_data_set_volume(new_data, &v);
779 pa_log_info("Restoring volume for sink %s: %s", new_data->name,
780 pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false));
782 new_data->save_volume = true;
784 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
787 if (u->restore_muted && e->muted_valid) {
789 if (!new_data->muted_is_set) {
790 pa_sink_new_data_set_muted(new_data, e->muted);
791 new_data->save_muted = true;
792 pa_log_info("Restoring mute state for sink %s: %smuted", new_data->name,
793 new_data->muted ? "" : "un");
795 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
798 perportentry_free(e);
806 static pa_hook_result_t sink_port_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
808 struct perportentry *e;
813 pa_assert(u->restore_volume || u->restore_muted);
815 name = pa_sprintf_malloc("sink:%s", sink->name);
817 if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
819 if (u->restore_volume && e->volume_valid) {
822 pa_log_info("Restoring volume for sink %s.", sink->name);
824 pa_cvolume_remap(&v, &e->channel_map, &sink->channel_map);
825 pa_sink_set_volume(sink, &v, true, false);
827 sink->save_volume = true;
830 if (u->restore_muted && e->muted_valid) {
832 pa_log_info("Restoring mute state for sink %s.", sink->name);
833 pa_sink_set_mute(sink, e->muted, false);
834 sink->save_muted = true;
837 perportentry_free(e);
845 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
847 struct perportentry *e;
852 pa_assert(u->restore_formats);
854 name = pa_sprintf_malloc("sink:%s", sink->name);
856 if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
858 if (!pa_sink_set_formats(sink, e->formats))
859 pa_log_debug("Could not set format on sink %s", sink->name);
861 perportentry_free(e);
869 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
876 pa_assert(u->restore_port);
878 name = pa_sprintf_malloc("source:%s", new_data->name);
880 if ((e = entry_read(u, name))) {
883 if (!new_data->active_port) {
884 pa_log_info("Restoring port for source %s.", name);
885 pa_source_new_data_set_port(new_data, e->port);
886 new_data->save_port = true;
888 pa_log_debug("Not restoring port for source %s, because already set.", name);
899 static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
901 struct perportentry *e;
906 pa_assert(u->restore_volume || u->restore_muted);
908 name = pa_sprintf_malloc("source:%s", new_data->name);
910 if ((e = perportentry_read(u, name, new_data->active_port))) {
912 if (u->restore_volume && e->volume_valid) {
914 if (!new_data->volume_is_set) {
916 char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
919 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
920 pa_source_new_data_set_volume(new_data, &v);
921 pa_log_info("Restoring volume for source %s: %s", new_data->name,
922 pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false));
924 new_data->save_volume = true;
926 pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
929 if (u->restore_muted && e->muted_valid) {
931 if (!new_data->muted_is_set) {
932 pa_source_new_data_set_muted(new_data, e->muted);
933 new_data->save_muted = true;
934 pa_log_info("Restoring mute state for source %s: %smuted", new_data->name,
935 new_data->muted ? "" : "un");
937 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
940 perportentry_free(e);
948 static pa_hook_result_t source_port_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
950 struct perportentry *e;
955 pa_assert(u->restore_volume || u->restore_muted);
957 name = pa_sprintf_malloc("source:%s", source->name);
959 if ((e = perportentry_read(u, name, (source->active_port ? source->active_port->name : NULL)))) {
961 if (u->restore_volume && e->volume_valid) {
964 pa_log_info("Restoring volume for source %s.", source->name);
966 pa_cvolume_remap(&v, &e->channel_map, &source->channel_map);
967 pa_source_set_volume(source, &v, true, false);
969 source->save_volume = true;
972 if (u->restore_muted && e->muted_valid) {
974 pa_log_info("Restoring mute state for source %s.", source->name);
975 pa_source_set_mute(source, e->muted, false);
976 source->save_muted = true;
979 perportentry_free(e);
987 #define EXT_VERSION 1
989 static void read_sink_format_reply(struct userdata *u, pa_tagstruct *reply, pa_sink *sink) {
990 struct perportentry *e;
997 pa_tagstruct_putu32(reply, PA_DEVICE_TYPE_SINK);
998 pa_tagstruct_putu32(reply, sink->index);
1000 /* Read or create an entry */
1001 name = pa_sprintf_malloc("sink:%s", sink->name);
1002 if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
1003 /* Fake a reply with PCM encoding supported */
1004 pa_format_info *f = pa_format_info_new();
1006 pa_tagstruct_putu8(reply, 1);
1007 f->encoding = PA_ENCODING_PCM;
1008 pa_tagstruct_put_format_info(reply, f);
1010 pa_format_info_free(f);
1015 /* Write all the formats from the entry to the reply */
1016 pa_tagstruct_putu8(reply, pa_idxset_size(e->formats));
1017 PA_IDXSET_FOREACH(f, e->formats, idx) {
1018 pa_tagstruct_put_format_info(reply, f);
1020 perportentry_free(e);
1025 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1028 pa_tagstruct *reply = NULL;
1037 if (pa_tagstruct_getu32(t, &command) < 0)
1040 reply = pa_tagstruct_new();
1041 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1042 pa_tagstruct_putu32(reply, tag);
1045 case SUBCOMMAND_TEST: {
1046 if (!pa_tagstruct_eof(t))
1049 pa_tagstruct_putu32(reply, EXT_VERSION);
1053 case SUBCOMMAND_SUBSCRIBE: {
1057 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1058 !pa_tagstruct_eof(t))
1062 pa_idxset_put(u->subscribed, c, NULL);
1064 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1069 case SUBCOMMAND_READ_FORMATS_ALL: {
1073 if (!pa_tagstruct_eof(t))
1076 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
1077 read_sink_format_reply(u, reply, sink);
1082 case SUBCOMMAND_READ_FORMATS: {
1083 pa_device_type_t type;
1084 uint32_t sink_index;
1089 /* Get the sink index and the number of formats from the tagstruct */
1090 if (pa_tagstruct_getu32(t, &type) < 0 ||
1091 pa_tagstruct_getu32(t, &sink_index) < 0)
1094 if (type != PA_DEVICE_TYPE_SINK) {
1095 pa_log("Device format reading is only supported on sinks");
1099 if (!pa_tagstruct_eof(t))
1102 /* Now find our sink */
1103 if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
1106 read_sink_format_reply(u, reply, sink);
1111 case SUBCOMMAND_SAVE_FORMATS: {
1113 struct perportentry *e;
1114 pa_device_type_t type;
1115 uint32_t sink_index;
1118 uint8_t i, n_formats;
1120 /* Get the sink index and the number of formats from the tagstruct */
1121 if (pa_tagstruct_getu32(t, &type) < 0 ||
1122 pa_tagstruct_getu32(t, &sink_index) < 0 ||
1123 pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
1128 if (type != PA_DEVICE_TYPE_SINK) {
1129 pa_log("Device format saving is only supported on sinks");
1133 /* Now find our sink */
1134 if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index))) {
1135 pa_log("Could not find sink #%d", sink_index);
1139 /* Read or create an entry */
1140 name = pa_sprintf_malloc("sink:%s", sink->name);
1141 if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL))))
1142 e = perportentry_new(false);
1144 /* Clean out any saved formats */
1145 pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free);
1146 e->formats = pa_idxset_new(NULL, NULL);
1149 /* Read all the formats from our tagstruct */
1150 for (i = 0; i < n_formats; ++i) {
1151 pa_format_info *f = pa_format_info_new();
1152 if (pa_tagstruct_get_format_info(t, f) < 0) {
1153 pa_format_info_free(f);
1154 perportentry_free(e);
1158 pa_idxset_put(e->formats, f, NULL);
1161 if (!pa_tagstruct_eof(t)) {
1162 perportentry_free(e);
1167 if (pa_sink_set_formats(sink, e->formats) && perportentry_write(u, name, (sink->active_port ? sink->active_port->name : NULL), e))
1168 trigger_save(u, type, sink_index);
1170 pa_log_warn("Could not save format info for sink %s", sink->name);
1173 perportentry_free(e);
1182 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1188 pa_tagstruct_free(reply);
1193 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1198 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1202 int pa__init(pa_module*m) {
1203 pa_modargs *ma = NULL;
1209 bool restore_volume = true, restore_muted = true, restore_port = true, restore_formats = true;
1213 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1214 pa_log("Failed to parse module arguments");
1218 if (pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
1219 pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
1220 pa_modargs_get_value_boolean(ma, "restore_port", &restore_port) < 0 ||
1221 pa_modargs_get_value_boolean(ma, "restore_formats", &restore_formats) < 0) {
1222 pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments");
1226 if (!restore_muted && !restore_volume && !restore_port && !restore_formats)
1227 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!");
1229 m->userdata = u = pa_xnew0(struct userdata, 1);
1232 u->restore_volume = restore_volume;
1233 u->restore_muted = restore_muted;
1234 u->restore_port = restore_port;
1235 u->restore_formats = restore_formats;
1237 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1239 u->protocol = pa_native_protocol_get(m->core);
1240 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1242 pa_module_hook_connect(m, &pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
1244 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
1247 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
1248 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
1251 if (restore_muted || restore_volume) {
1252 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_fixate_hook_callback, u);
1253 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) source_fixate_hook_callback, u);
1255 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) sink_port_hook_callback, u);
1256 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) source_port_hook_callback, u);
1259 if (restore_formats)
1260 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_put_hook_callback, u);
1262 if (!(state_path = pa_state_path(NULL, true)))
1265 if (!(u->database = pa_database_open(state_path, "device-volumes", true, true))) {
1266 pa_xfree(state_path);
1270 pa_xfree(state_path);
1272 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1273 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1275 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1276 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1278 pa_modargs_free(ma);
1285 pa_modargs_free(ma);
1290 void pa__done(pa_module*m) {
1295 if (!(u = m->userdata))
1298 if (u->subscription)
1299 pa_subscription_free(u->subscription);
1301 if (u->save_time_event) {
1302 u->core->mainloop->time_free(u->save_time_event);
1303 pa_database_sync(u->database);
1307 pa_database_close(u->database);
1310 pa_native_protocol_remove_ext(u->protocol, m);
1311 pa_native_protocol_unref(u->protocol);
1315 pa_idxset_free(u->subscribed, NULL);