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 struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port);
181 static pa_bool_t perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e);
182 static void perportentry_free(struct perportentry* e);
185 static struct entry* entry_new() {
186 struct entry *r = pa_xnew0(struct entry, 1);
187 r->version = ENTRY_VERSION;
191 static void entry_free(struct entry* e) {
198 static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
207 t = pa_tagstruct_new(NULL, 0);
208 pa_tagstruct_putu8(t, e->version);
209 pa_tagstruct_put_boolean(t, e->port_valid);
210 pa_tagstruct_puts(t, e->port);
212 key.data = (char *) name;
213 key.size = strlen(name);
215 data.data = (void*)pa_tagstruct_data(t, &data.size);
217 r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
219 pa_tagstruct_free(t);
224 static struct entry* entry_read(struct userdata *u, const char *name) {
226 struct entry *e = NULL;
227 pa_tagstruct *t = NULL;
233 key.data = (char*) name;
234 key.size = strlen(name);
238 if (!pa_database_get(u->database, &key, &data))
241 t = pa_tagstruct_new(data.data, data.size);
242 e = entry_new(FALSE);
244 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
245 e->version > ENTRY_VERSION ||
246 pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
247 pa_tagstruct_gets(t, &port) < 0) {
252 if (!pa_tagstruct_eof(t))
255 e->port = pa_xstrdup(port);
257 pa_tagstruct_free(t);
258 pa_datum_free(&data);
264 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
269 pa_tagstruct_free(t);
271 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
273 struct perportentry *ppe;
274 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
275 if (legacy_entry_read(u, &data, &e, &ppe)) {
276 pa_bool_t written = FALSE;
278 pa_log_debug("Success. Saving new format for key: %s", name);
279 written = entry_write(u, name, e);
281 /* Now convert the legacy entry into per-port entries */
282 if (0 == strncmp("sink:", name, 5)) {
285 if ((sink = pa_namereg_get(u->core, name+5, PA_NAMEREG_SINK))) {
286 /* Write a "null" port entry. The read code will automatically try this
287 * if it cannot find a specific port-named entry. */
288 written = perportentry_write(u, name, NULL, ppe) || written;
290 } else if (0 == strncmp("source:", name, 7)) {
293 if ((source = pa_namereg_get(u->core, name+7, PA_NAMEREG_SOURCE))) {
294 /* Write a "null" port entry. The read code will automatically try this
295 * if it cannot find a specific port-named entry. */
296 written = perportentry_write(u, name, NULL, ppe) || written;
299 perportentry_free(ppe);
302 /* NB The device type doesn't matter when we pass in an invalid index. */
303 trigger_save(u, PA_DEVICE_TYPE_SINK, PA_INVALID_INDEX);
305 pa_datum_free(&data);
308 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
312 pa_datum_free(&data);
316 static struct entry* entry_copy(const struct entry *e) {
321 r->version = e->version;
322 r->port_valid = e->port_valid;
323 r->port = pa_xstrdup(e->port);
328 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
332 if (a->port_valid != b->port_valid ||
333 (a->port_valid && !pa_streq(a->port, b->port)))
339 static struct perportentry* perportentry_new(pa_bool_t add_pcm_format) {
340 struct perportentry *r = pa_xnew0(struct perportentry, 1);
341 r->version = PERPORTENTRY_VERSION;
342 r->formats = pa_idxset_new(NULL, NULL);
343 if (add_pcm_format) {
344 pa_format_info *f = pa_format_info_new();
345 f->encoding = PA_ENCODING_PCM;
346 pa_idxset_put(r->formats, f, NULL);
351 static void perportentry_free(struct perportentry* e) {
354 pa_idxset_free(e->formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
358 static pa_bool_t perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e) {
368 pa_assert(basekeyname);
371 name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
373 n_formats = pa_idxset_size(e->formats);
374 pa_assert(n_formats > 0);
376 t = pa_tagstruct_new(NULL, 0);
377 pa_tagstruct_putu8(t, e->version);
378 pa_tagstruct_put_boolean(t, e->volume_valid);
379 pa_tagstruct_put_channel_map(t, &e->channel_map);
380 pa_tagstruct_put_cvolume(t, &e->volume);
381 pa_tagstruct_put_boolean(t, e->muted_valid);
382 pa_tagstruct_put_boolean(t, e->muted);
383 pa_tagstruct_putu8(t, n_formats);
385 PA_IDXSET_FOREACH(f, e->formats, i) {
386 pa_tagstruct_put_format_info(t, f);
389 key.data = (char *) name;
390 key.size = strlen(name);
392 data.data = (void*)pa_tagstruct_data(t, &data.size);
394 r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
396 pa_tagstruct_free(t);
402 static struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port) {
404 struct perportentry *e = NULL;
405 pa_tagstruct *t = NULL;
406 uint8_t i, n_formats;
410 pa_assert(basekeyname);
412 name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null"));
415 key.size = strlen(name);
419 if (!pa_database_get(u->database, &key, &data))
422 t = pa_tagstruct_new(data.data, data.size);
423 e = perportentry_new(FALSE);
425 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
426 e->version > PERPORTENTRY_VERSION ||
427 pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
428 pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
429 pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
430 pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
431 pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
432 pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
437 for (i = 0; i < n_formats; ++i) {
438 pa_format_info *f = pa_format_info_new();
439 if (pa_tagstruct_get_format_info(t, f) < 0) {
440 pa_format_info_free(f);
443 pa_idxset_put(e->formats, f, NULL);
446 if (!pa_tagstruct_eof(t))
449 if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
450 pa_log_warn("Invalid channel map stored in database for device %s", name);
454 if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
455 pa_log_warn("Volume and channel map don't match in database entry for device %s", name);
459 pa_tagstruct_free(t);
460 pa_datum_free(&data);
468 perportentry_free(e);
470 pa_tagstruct_free(t);
472 pa_datum_free(&data);
474 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
475 /* Try again with a null port. This is used when dealing with migration from older versions */
478 return perportentry_read(u, basekeyname, NULL);
482 pa_log_debug("Database contains invalid data for key: %s", name);
489 static struct perportentry* perportentry_copy(const struct perportentry *e) {
490 struct perportentry* r;
495 r = perportentry_new(FALSE);
496 r->version = e->version;
497 r->muted_valid = e->muted_valid;
498 r->volume_valid = e->volume_valid;
500 r->channel_map = e->channel_map;
501 r->volume = e->volume;
503 PA_IDXSET_FOREACH(f, e->formats, idx) {
504 pa_idxset_put(r->formats, pa_format_info_copy(f), NULL);
509 static pa_bool_t perportentries_equal(const struct perportentry *a, const struct perportentry *b) {
514 if (a->muted_valid != b->muted_valid ||
515 (a->muted_valid && (a->muted != b->muted)))
519 if (a->volume_valid != b->volume_valid ||
520 (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
523 if (pa_idxset_size(a->formats) != pa_idxset_size(b->formats))
526 /** TODO: Compare a bit better */
531 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
533 #define LEGACY_ENTRY_VERSION 2
534 static pa_bool_t legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry) {
535 struct legacy_entry {
537 pa_bool_t muted_valid:1, volume_valid:1, port_valid:1;
539 pa_channel_map channel_map;
541 char port[PA_NAME_MAX];
543 struct legacy_entry *le;
548 pa_assert(perportentry);
550 if (data->size != sizeof(struct legacy_entry)) {
551 pa_log_debug("Size does not match.");
555 le = (struct legacy_entry*)data->data;
557 if (le->version != LEGACY_ENTRY_VERSION) {
558 pa_log_debug("Version mismatch.");
562 if (!memchr(le->port, 0, sizeof(le->port))) {
563 pa_log_warn("Port has missing NUL byte.");
567 if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
568 pa_log_warn("Invalid channel map.");
572 if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
573 pa_log_warn("Volume and channel map don't match.");
577 *entry = entry_new();
578 (*entry)->port_valid = le->port_valid;
579 (*entry)->port = pa_xstrdup(le->port);
581 *perportentry = perportentry_new(TRUE);
582 (*perportentry)->muted_valid = le->muted_valid;
583 (*perportentry)->volume_valid = le->volume_valid;
584 (*perportentry)->muted = le->muted;
585 (*perportentry)->channel_map = le->channel_map;
586 (*perportentry)->volume = le->volume;
592 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
593 struct userdata *u = userdata;
594 struct entry *e, *olde;
595 struct perportentry *ppe, *oldppe;
597 const char *port = NULL;
598 pa_device_type_t type;
599 pa_bool_t written = FALSE;
604 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
605 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
606 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
607 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
610 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
613 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
616 type = PA_DEVICE_TYPE_SINK;
617 name = pa_sprintf_malloc("sink:%s", sink->name);
618 if (sink->active_port)
619 port = sink->active_port->name;
621 if ((olde = entry_read(u, name)))
622 e = entry_copy(olde);
626 if (sink->save_port) {
628 e->port = pa_xstrdup(port ? port : "");
629 e->port_valid = TRUE;
632 if ((oldppe = perportentry_read(u, name, port)))
633 ppe = perportentry_copy(oldppe);
635 ppe = perportentry_new(TRUE);
637 if (sink->save_volume) {
638 ppe->channel_map = sink->channel_map;
639 ppe->volume = *pa_sink_get_volume(sink, FALSE);
640 ppe->volume_valid = TRUE;
643 if (sink->save_muted) {
644 ppe->muted = pa_sink_get_mute(sink, FALSE);
645 ppe->muted_valid = TRUE;
650 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
652 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
655 type = PA_DEVICE_TYPE_SOURCE;
656 name = pa_sprintf_malloc("source:%s", source->name);
657 if (source->active_port)
658 port = source->active_port->name;
660 if ((olde = entry_read(u, name)))
661 e = entry_copy(olde);
665 if (source->save_port) {
667 e->port = pa_xstrdup(port ? port : "");
668 e->port_valid = TRUE;
671 if ((oldppe = perportentry_read(u, name, port)))
672 ppe = perportentry_copy(oldppe);
674 ppe = perportentry_new(TRUE);
676 if (source->save_volume) {
677 ppe->channel_map = source->channel_map;
678 ppe->volume = *pa_source_get_volume(source, FALSE);
679 ppe->volume_valid = TRUE;
682 if (source->save_muted) {
683 ppe->muted = pa_source_get_mute(source, FALSE);
684 ppe->muted_valid = TRUE;
693 if (entries_equal(olde, e)) {
702 pa_log_info("Storing port for device %s.", name);
704 written = entry_write(u, name, e);
714 if (perportentries_equal(oldppe, ppe)) {
715 perportentry_free(oldppe);
716 perportentry_free(ppe);
719 perportentry_free(oldppe);
723 pa_log_info("Storing volume/mute for device+port %s:%s.", name, (port ? port : "null"));
725 written = perportentry_write(u, name, port, ppe) || written;
727 perportentry_free(ppe);
732 trigger_save(u, type, idx);
735 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
742 pa_assert(u->restore_port);
744 name = pa_sprintf_malloc("sink:%s", new_data->name);
746 if ((e = entry_read(u, name))) {
749 if (!new_data->active_port) {
750 pa_log_info("Restoring port for sink %s.", name);
751 pa_sink_new_data_set_port(new_data, e->port);
752 new_data->save_port = TRUE;
754 pa_log_debug("Not restoring port for sink %s, because already set.", name);
765 static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
767 struct perportentry *e;
772 pa_assert(u->restore_volume || u->restore_muted);
774 name = pa_sprintf_malloc("sink:%s", new_data->name);
776 if ((e = perportentry_read(u, name, new_data->active_port))) {
778 if (u->restore_volume && e->volume_valid) {
780 if (!new_data->volume_is_set) {
783 pa_log_info("Restoring volume for sink %s.", new_data->name);
786 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
787 pa_sink_new_data_set_volume(new_data, &v);
789 new_data->save_volume = TRUE;
791 pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
794 if (u->restore_muted && e->muted_valid) {
796 if (!new_data->muted_is_set) {
797 pa_log_info("Restoring mute state for sink %s.", new_data->name);
798 pa_sink_new_data_set_muted(new_data, e->muted);
799 new_data->save_muted = TRUE;
801 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
804 perportentry_free(e);
812 static pa_hook_result_t sink_port_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
814 struct perportentry *e;
819 pa_assert(u->restore_volume || u->restore_muted);
821 name = pa_sprintf_malloc("sink:%s", sink->name);
823 if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
825 if (u->restore_volume && e->volume_valid) {
829 pa_log_info("Restoring volume for sink %s.", sink->name);
832 pa_cvolume_remap(&v, &e->channel_map, &sink->channel_map);
833 pa_sink_set_volume(sink, &v, TRUE, FALSE);
834 sink->save_volume = TRUE;
837 if (u->restore_muted && e->muted_valid) {
839 pa_log_info("Restoring mute state for sink %s.", sink->name);
840 pa_sink_set_mute(sink, e->muted, FALSE);
841 sink->save_muted = TRUE;
844 perportentry_free(e);
852 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
854 struct perportentry *e;
859 pa_assert(u->restore_formats);
861 name = pa_sprintf_malloc("sink:%s", sink->name);
863 if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) {
865 if (!pa_sink_set_formats(sink, e->formats))
866 pa_log_debug("Could not set format on sink %s", sink->name);
868 perportentry_free(e);
876 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
883 pa_assert(u->restore_port);
885 name = pa_sprintf_malloc("source:%s", new_data->name);
887 if ((e = entry_read(u, name))) {
890 if (!new_data->active_port) {
891 pa_log_info("Restoring port for source %s.", name);
892 pa_source_new_data_set_port(new_data, e->port);
893 new_data->save_port = TRUE;
895 pa_log_debug("Not restoring port for source %s, because already set.", name);
906 static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
908 struct perportentry *e;
913 pa_assert(u->restore_volume || u->restore_muted);
915 name = pa_sprintf_malloc("source:%s", new_data->name);
917 if ((e = perportentry_read(u, name, new_data->active_port))) {
919 if (u->restore_volume && e->volume_valid) {
921 if (!new_data->volume_is_set) {
924 pa_log_info("Restoring volume for source %s.", new_data->name);
927 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
928 pa_source_new_data_set_volume(new_data, &v);
930 new_data->save_volume = TRUE;
932 pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
935 if (u->restore_muted && e->muted_valid) {
937 if (!new_data->muted_is_set) {
938 pa_log_info("Restoring mute state for source %s.", new_data->name);
939 pa_source_new_data_set_muted(new_data, e->muted);
940 new_data->save_muted = TRUE;
942 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
945 perportentry_free(e);
953 static pa_hook_result_t source_port_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
955 struct perportentry *e;
960 pa_assert(u->restore_volume || u->restore_muted);
962 name = pa_sprintf_malloc("source:%s", source->name);
964 if ((e = perportentry_read(u, name, (source->active_port ? source->active_port->name : NULL)))) {
966 if (u->restore_volume && e->volume_valid) {
970 pa_log_info("Restoring volume for source %s.", source->name);
973 pa_cvolume_remap(&v, &e->channel_map, &source->channel_map);
974 pa_source_set_volume(source, &v, TRUE, FALSE);
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_free2_cb_t) pa_format_info_free2, NULL);
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 for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
1279 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1281 for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(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, NULL);