153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2006-2008 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2011 Colin Guthrie 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 953a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1053a5a1b3Sopenharmony_ci or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1853a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <unistd.h> 2653a5a1b3Sopenharmony_ci#include <string.h> 2753a5a1b3Sopenharmony_ci#include <errno.h> 2853a5a1b3Sopenharmony_ci#include <sys/types.h> 2953a5a1b3Sopenharmony_ci#include <stdio.h> 3053a5a1b3Sopenharmony_ci#include <stdlib.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h> 3353a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3453a5a1b3Sopenharmony_ci#include <pulse/volume.h> 3553a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 3653a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 3753a5a1b3Sopenharmony_ci#include <pulse/format.h> 3853a5a1b3Sopenharmony_ci#include <pulse/internal.h> 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 4753a5a1b3Sopenharmony_ci#include <pulsecore/source.h> 4853a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 4953a5a1b3Sopenharmony_ci#include <pulsecore/protocol-native.h> 5053a5a1b3Sopenharmony_ci#include <pulsecore/pstream.h> 5153a5a1b3Sopenharmony_ci#include <pulsecore/pstream-util.h> 5253a5a1b3Sopenharmony_ci#include <pulsecore/database.h> 5353a5a1b3Sopenharmony_ci#include <pulsecore/tagstruct.h> 5453a5a1b3Sopenharmony_ci 5553a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering"); 5653a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices"); 5753a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 5853a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true); 5953a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 6053a5a1b3Sopenharmony_ci "restore_port=<Save/restore port?> " 6153a5a1b3Sopenharmony_ci "restore_volume=<Save/restore volumes?> " 6253a5a1b3Sopenharmony_ci "restore_muted=<Save/restore muted states?> " 6353a5a1b3Sopenharmony_ci "restore_formats=<Save/restore saved formats?>"); 6453a5a1b3Sopenharmony_ci 6553a5a1b3Sopenharmony_ci#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 6853a5a1b3Sopenharmony_ci "restore_volume", 6953a5a1b3Sopenharmony_ci "restore_muted", 7053a5a1b3Sopenharmony_ci "restore_port", 7153a5a1b3Sopenharmony_ci "restore_formats", 7253a5a1b3Sopenharmony_ci NULL 7353a5a1b3Sopenharmony_ci}; 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_cistruct userdata { 7653a5a1b3Sopenharmony_ci pa_core *core; 7753a5a1b3Sopenharmony_ci pa_module *module; 7853a5a1b3Sopenharmony_ci pa_subscription *subscription; 7953a5a1b3Sopenharmony_ci pa_time_event *save_time_event; 8053a5a1b3Sopenharmony_ci pa_database *database; 8153a5a1b3Sopenharmony_ci 8253a5a1b3Sopenharmony_ci pa_native_protocol *protocol; 8353a5a1b3Sopenharmony_ci pa_idxset *subscribed; 8453a5a1b3Sopenharmony_ci 8553a5a1b3Sopenharmony_ci bool restore_volume:1; 8653a5a1b3Sopenharmony_ci bool restore_muted:1; 8753a5a1b3Sopenharmony_ci bool restore_port:1; 8853a5a1b3Sopenharmony_ci bool restore_formats:1; 8953a5a1b3Sopenharmony_ci}; 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_ci/* Protocol extension commands */ 9253a5a1b3Sopenharmony_cienum { 9353a5a1b3Sopenharmony_ci SUBCOMMAND_TEST, 9453a5a1b3Sopenharmony_ci SUBCOMMAND_SUBSCRIBE, 9553a5a1b3Sopenharmony_ci SUBCOMMAND_EVENT, 9653a5a1b3Sopenharmony_ci SUBCOMMAND_READ_FORMATS_ALL, 9753a5a1b3Sopenharmony_ci SUBCOMMAND_READ_FORMATS, 9853a5a1b3Sopenharmony_ci SUBCOMMAND_SAVE_FORMATS 9953a5a1b3Sopenharmony_ci}; 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci#define ENTRY_VERSION 1 10253a5a1b3Sopenharmony_ci 10353a5a1b3Sopenharmony_cistruct entry { 10453a5a1b3Sopenharmony_ci uint8_t version; 10553a5a1b3Sopenharmony_ci bool port_valid; 10653a5a1b3Sopenharmony_ci char *port; 10753a5a1b3Sopenharmony_ci}; 10853a5a1b3Sopenharmony_ci 10953a5a1b3Sopenharmony_ci#define PERPORTENTRY_VERSION 1 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_cistruct perportentry { 11253a5a1b3Sopenharmony_ci uint8_t version; 11353a5a1b3Sopenharmony_ci bool muted_valid, volume_valid; 11453a5a1b3Sopenharmony_ci bool muted; 11553a5a1b3Sopenharmony_ci pa_channel_map channel_map; 11653a5a1b3Sopenharmony_ci pa_cvolume volume; 11753a5a1b3Sopenharmony_ci pa_idxset *formats; 11853a5a1b3Sopenharmony_ci}; 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_cistatic void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { 12153a5a1b3Sopenharmony_ci struct userdata *u = userdata; 12253a5a1b3Sopenharmony_ci 12353a5a1b3Sopenharmony_ci pa_assert(a); 12453a5a1b3Sopenharmony_ci pa_assert(e); 12553a5a1b3Sopenharmony_ci pa_assert(u); 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci pa_assert(e == u->save_time_event); 12853a5a1b3Sopenharmony_ci u->core->mainloop->time_free(u->save_time_event); 12953a5a1b3Sopenharmony_ci u->save_time_event = NULL; 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci pa_database_sync(u->database); 13253a5a1b3Sopenharmony_ci pa_log_info("Synced."); 13353a5a1b3Sopenharmony_ci} 13453a5a1b3Sopenharmony_ci 13553a5a1b3Sopenharmony_cistatic void trigger_save(struct userdata *u, pa_device_type_t type, uint32_t sink_idx) { 13653a5a1b3Sopenharmony_ci pa_native_connection *c; 13753a5a1b3Sopenharmony_ci uint32_t idx; 13853a5a1b3Sopenharmony_ci 13953a5a1b3Sopenharmony_ci if (sink_idx != PA_INVALID_INDEX) { 14053a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(c, u->subscribed, idx) { 14153a5a1b3Sopenharmony_ci pa_tagstruct *t; 14253a5a1b3Sopenharmony_ci 14353a5a1b3Sopenharmony_ci t = pa_tagstruct_new(); 14453a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION); 14553a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, 0); 14653a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, u->module->index); 14753a5a1b3Sopenharmony_ci pa_tagstruct_puts(t, u->module->name); 14853a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, SUBCOMMAND_EVENT); 14953a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, type); 15053a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, sink_idx); 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_ci pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t); 15353a5a1b3Sopenharmony_ci } 15453a5a1b3Sopenharmony_ci } 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci if (u->save_time_event) 15753a5a1b3Sopenharmony_ci return; 15853a5a1b3Sopenharmony_ci 15953a5a1b3Sopenharmony_ci u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); 16053a5a1b3Sopenharmony_ci} 16153a5a1b3Sopenharmony_ci 16253a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 16353a5a1b3Sopenharmony_ci/* Some forward declarations */ 16453a5a1b3Sopenharmony_cistatic bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry); 16553a5a1b3Sopenharmony_cistatic struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port); 16653a5a1b3Sopenharmony_cistatic bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e); 16753a5a1b3Sopenharmony_cistatic void perportentry_free(struct perportentry* e); 16853a5a1b3Sopenharmony_ci#endif 16953a5a1b3Sopenharmony_ci 17053a5a1b3Sopenharmony_cistatic struct entry* entry_new(void) { 17153a5a1b3Sopenharmony_ci struct entry *r = pa_xnew0(struct entry, 1); 17253a5a1b3Sopenharmony_ci r->version = ENTRY_VERSION; 17353a5a1b3Sopenharmony_ci return r; 17453a5a1b3Sopenharmony_ci} 17553a5a1b3Sopenharmony_ci 17653a5a1b3Sopenharmony_cistatic void entry_free(struct entry* e) { 17753a5a1b3Sopenharmony_ci pa_assert(e); 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci pa_xfree(e->port); 18053a5a1b3Sopenharmony_ci pa_xfree(e); 18153a5a1b3Sopenharmony_ci} 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_cistatic bool entry_write(struct userdata *u, const char *name, const struct entry *e) { 18453a5a1b3Sopenharmony_ci pa_tagstruct *t; 18553a5a1b3Sopenharmony_ci pa_datum key, data; 18653a5a1b3Sopenharmony_ci bool r; 18753a5a1b3Sopenharmony_ci 18853a5a1b3Sopenharmony_ci pa_assert(u); 18953a5a1b3Sopenharmony_ci pa_assert(name); 19053a5a1b3Sopenharmony_ci pa_assert(e); 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci t = pa_tagstruct_new(); 19353a5a1b3Sopenharmony_ci pa_tagstruct_putu8(t, e->version); 19453a5a1b3Sopenharmony_ci pa_tagstruct_put_boolean(t, e->port_valid); 19553a5a1b3Sopenharmony_ci pa_tagstruct_puts(t, e->port); 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_ci key.data = (char *) name; 19853a5a1b3Sopenharmony_ci key.size = strlen(name); 19953a5a1b3Sopenharmony_ci 20053a5a1b3Sopenharmony_ci data.data = (void*)pa_tagstruct_data(t, &data.size); 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_ci r = (pa_database_set(u->database, &key, &data, true) == 0); 20353a5a1b3Sopenharmony_ci 20453a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_ci return r; 20753a5a1b3Sopenharmony_ci} 20853a5a1b3Sopenharmony_ci 20953a5a1b3Sopenharmony_cistatic struct entry* entry_read(struct userdata *u, const char *name) { 21053a5a1b3Sopenharmony_ci pa_datum key, data; 21153a5a1b3Sopenharmony_ci struct entry *e = NULL; 21253a5a1b3Sopenharmony_ci pa_tagstruct *t = NULL; 21353a5a1b3Sopenharmony_ci const char* port; 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci pa_assert(u); 21653a5a1b3Sopenharmony_ci pa_assert(name); 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci key.data = (char*) name; 21953a5a1b3Sopenharmony_ci key.size = strlen(name); 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci pa_zero(data); 22253a5a1b3Sopenharmony_ci 22353a5a1b3Sopenharmony_ci if (!pa_database_get(u->database, &key, &data)) { 22453a5a1b3Sopenharmony_ci pa_log_debug("Database contains no data for key: %s", name); 22553a5a1b3Sopenharmony_ci return NULL; 22653a5a1b3Sopenharmony_ci } 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci t = pa_tagstruct_new_fixed(data.data, data.size); 22953a5a1b3Sopenharmony_ci e = entry_new(); 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_ci if (pa_tagstruct_getu8(t, &e->version) < 0 || 23253a5a1b3Sopenharmony_ci e->version > ENTRY_VERSION || 23353a5a1b3Sopenharmony_ci pa_tagstruct_get_boolean(t, &e->port_valid) < 0 || 23453a5a1b3Sopenharmony_ci pa_tagstruct_gets(t, &port) < 0) { 23553a5a1b3Sopenharmony_ci 23653a5a1b3Sopenharmony_ci goto fail; 23753a5a1b3Sopenharmony_ci } 23853a5a1b3Sopenharmony_ci 23953a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 24053a5a1b3Sopenharmony_ci goto fail; 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci e->port = pa_xstrdup(port); 24353a5a1b3Sopenharmony_ci 24453a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 24553a5a1b3Sopenharmony_ci pa_datum_free(&data); 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci return e; 24853a5a1b3Sopenharmony_ci 24953a5a1b3Sopenharmony_cifail: 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_ci pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name); 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci if (e) 25453a5a1b3Sopenharmony_ci entry_free(e); 25553a5a1b3Sopenharmony_ci if (t) 25653a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 25953a5a1b3Sopenharmony_ci{ 26053a5a1b3Sopenharmony_ci struct perportentry *ppe; 26153a5a1b3Sopenharmony_ci pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name); 26253a5a1b3Sopenharmony_ci if (legacy_entry_read(u, &data, &e, &ppe)) { 26353a5a1b3Sopenharmony_ci bool written = false; 26453a5a1b3Sopenharmony_ci 26553a5a1b3Sopenharmony_ci pa_log_debug("Success. Saving new format for key: %s", name); 26653a5a1b3Sopenharmony_ci written = entry_write(u, name, e); 26753a5a1b3Sopenharmony_ci 26853a5a1b3Sopenharmony_ci /* Now convert the legacy entry into per-port entries */ 26953a5a1b3Sopenharmony_ci if (0 == strncmp("sink:", name, 5)) { 27053a5a1b3Sopenharmony_ci pa_sink *sink; 27153a5a1b3Sopenharmony_ci 27253a5a1b3Sopenharmony_ci if ((sink = pa_namereg_get(u->core, name+5, PA_NAMEREG_SINK))) { 27353a5a1b3Sopenharmony_ci /* Write a "null" port entry. The read code will automatically try this 27453a5a1b3Sopenharmony_ci * if it cannot find a specific port-named entry. */ 27553a5a1b3Sopenharmony_ci written = perportentry_write(u, name, NULL, ppe) || written; 27653a5a1b3Sopenharmony_ci } 27753a5a1b3Sopenharmony_ci } else if (0 == strncmp("source:", name, 7)) { 27853a5a1b3Sopenharmony_ci pa_source *source; 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci if ((source = pa_namereg_get(u->core, name+7, PA_NAMEREG_SOURCE))) { 28153a5a1b3Sopenharmony_ci /* Write a "null" port entry. The read code will automatically try this 28253a5a1b3Sopenharmony_ci * if it cannot find a specific port-named entry. */ 28353a5a1b3Sopenharmony_ci written = perportentry_write(u, name, NULL, ppe) || written; 28453a5a1b3Sopenharmony_ci } 28553a5a1b3Sopenharmony_ci } 28653a5a1b3Sopenharmony_ci perportentry_free(ppe); 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_ci if (written) 28953a5a1b3Sopenharmony_ci /* NB The device type doesn't matter when we pass in an invalid index. */ 29053a5a1b3Sopenharmony_ci trigger_save(u, PA_DEVICE_TYPE_SINK, PA_INVALID_INDEX); 29153a5a1b3Sopenharmony_ci 29253a5a1b3Sopenharmony_ci pa_datum_free(&data); 29353a5a1b3Sopenharmony_ci return e; 29453a5a1b3Sopenharmony_ci } 29553a5a1b3Sopenharmony_ci pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name); 29653a5a1b3Sopenharmony_ci} 29753a5a1b3Sopenharmony_ci#endif 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ci pa_datum_free(&data); 30053a5a1b3Sopenharmony_ci return NULL; 30153a5a1b3Sopenharmony_ci} 30253a5a1b3Sopenharmony_ci 30353a5a1b3Sopenharmony_cistatic struct entry* entry_copy(const struct entry *e) { 30453a5a1b3Sopenharmony_ci struct entry* r; 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_ci pa_assert(e); 30753a5a1b3Sopenharmony_ci r = entry_new(); 30853a5a1b3Sopenharmony_ci r->version = e->version; 30953a5a1b3Sopenharmony_ci r->port_valid = e->port_valid; 31053a5a1b3Sopenharmony_ci r->port = pa_xstrdup(e->port); 31153a5a1b3Sopenharmony_ci 31253a5a1b3Sopenharmony_ci return r; 31353a5a1b3Sopenharmony_ci} 31453a5a1b3Sopenharmony_ci 31553a5a1b3Sopenharmony_cistatic bool entries_equal(const struct entry *a, const struct entry *b) { 31653a5a1b3Sopenharmony_ci 31753a5a1b3Sopenharmony_ci pa_assert(a && b); 31853a5a1b3Sopenharmony_ci 31953a5a1b3Sopenharmony_ci if (a->port_valid != b->port_valid || 32053a5a1b3Sopenharmony_ci (a->port_valid && !pa_streq(a->port, b->port))) 32153a5a1b3Sopenharmony_ci return false; 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci return true; 32453a5a1b3Sopenharmony_ci} 32553a5a1b3Sopenharmony_ci 32653a5a1b3Sopenharmony_cistatic struct perportentry* perportentry_new(bool add_pcm_format) { 32753a5a1b3Sopenharmony_ci struct perportentry *r = pa_xnew0(struct perportentry, 1); 32853a5a1b3Sopenharmony_ci r->version = PERPORTENTRY_VERSION; 32953a5a1b3Sopenharmony_ci r->formats = pa_idxset_new(NULL, NULL); 33053a5a1b3Sopenharmony_ci if (add_pcm_format) { 33153a5a1b3Sopenharmony_ci pa_format_info *f = pa_format_info_new(); 33253a5a1b3Sopenharmony_ci f->encoding = PA_ENCODING_PCM; 33353a5a1b3Sopenharmony_ci pa_idxset_put(r->formats, f, NULL); 33453a5a1b3Sopenharmony_ci } 33553a5a1b3Sopenharmony_ci return r; 33653a5a1b3Sopenharmony_ci} 33753a5a1b3Sopenharmony_ci 33853a5a1b3Sopenharmony_cistatic void perportentry_free(struct perportentry* e) { 33953a5a1b3Sopenharmony_ci pa_assert(e); 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free); 34253a5a1b3Sopenharmony_ci pa_xfree(e); 34353a5a1b3Sopenharmony_ci} 34453a5a1b3Sopenharmony_ci 34553a5a1b3Sopenharmony_cistatic bool perportentry_write(struct userdata *u, const char *basekeyname, const char *port, const struct perportentry *e) { 34653a5a1b3Sopenharmony_ci pa_tagstruct *t; 34753a5a1b3Sopenharmony_ci pa_datum key, data; 34853a5a1b3Sopenharmony_ci bool r; 34953a5a1b3Sopenharmony_ci uint32_t i; 35053a5a1b3Sopenharmony_ci pa_format_info *f; 35153a5a1b3Sopenharmony_ci uint8_t n_formats; 35253a5a1b3Sopenharmony_ci char *name; 35353a5a1b3Sopenharmony_ci 35453a5a1b3Sopenharmony_ci pa_assert(u); 35553a5a1b3Sopenharmony_ci pa_assert(basekeyname); 35653a5a1b3Sopenharmony_ci pa_assert(e); 35753a5a1b3Sopenharmony_ci 35853a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null")); 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci n_formats = pa_idxset_size(e->formats); 36153a5a1b3Sopenharmony_ci pa_assert(n_formats > 0); 36253a5a1b3Sopenharmony_ci 36353a5a1b3Sopenharmony_ci t = pa_tagstruct_new(); 36453a5a1b3Sopenharmony_ci pa_tagstruct_putu8(t, e->version); 36553a5a1b3Sopenharmony_ci pa_tagstruct_put_boolean(t, e->volume_valid); 36653a5a1b3Sopenharmony_ci pa_tagstruct_put_channel_map(t, &e->channel_map); 36753a5a1b3Sopenharmony_ci pa_tagstruct_put_cvolume(t, &e->volume); 36853a5a1b3Sopenharmony_ci pa_tagstruct_put_boolean(t, e->muted_valid); 36953a5a1b3Sopenharmony_ci pa_tagstruct_put_boolean(t, e->muted); 37053a5a1b3Sopenharmony_ci pa_tagstruct_putu8(t, n_formats); 37153a5a1b3Sopenharmony_ci 37253a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(f, e->formats, i) { 37353a5a1b3Sopenharmony_ci pa_tagstruct_put_format_info(t, f); 37453a5a1b3Sopenharmony_ci } 37553a5a1b3Sopenharmony_ci 37653a5a1b3Sopenharmony_ci key.data = (char *) name; 37753a5a1b3Sopenharmony_ci key.size = strlen(name); 37853a5a1b3Sopenharmony_ci 37953a5a1b3Sopenharmony_ci data.data = (void*)pa_tagstruct_data(t, &data.size); 38053a5a1b3Sopenharmony_ci 38153a5a1b3Sopenharmony_ci r = (pa_database_set(u->database, &key, &data, true) == 0); 38253a5a1b3Sopenharmony_ci 38353a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 38453a5a1b3Sopenharmony_ci pa_xfree(name); 38553a5a1b3Sopenharmony_ci 38653a5a1b3Sopenharmony_ci return r; 38753a5a1b3Sopenharmony_ci} 38853a5a1b3Sopenharmony_ci 38953a5a1b3Sopenharmony_cistatic struct perportentry* perportentry_read(struct userdata *u, const char *basekeyname, const char *port) { 39053a5a1b3Sopenharmony_ci pa_datum key, data; 39153a5a1b3Sopenharmony_ci struct perportentry *e = NULL; 39253a5a1b3Sopenharmony_ci pa_tagstruct *t = NULL; 39353a5a1b3Sopenharmony_ci uint8_t i, n_formats; 39453a5a1b3Sopenharmony_ci char *name; 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_ci pa_assert(u); 39753a5a1b3Sopenharmony_ci pa_assert(basekeyname); 39853a5a1b3Sopenharmony_ci 39953a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("%s:%s", basekeyname, (port ? port : "null")); 40053a5a1b3Sopenharmony_ci 40153a5a1b3Sopenharmony_ci key.data = name; 40253a5a1b3Sopenharmony_ci key.size = strlen(name); 40353a5a1b3Sopenharmony_ci 40453a5a1b3Sopenharmony_ci pa_zero(data); 40553a5a1b3Sopenharmony_ci 40653a5a1b3Sopenharmony_ci if (!pa_database_get(u->database, &key, &data)) 40753a5a1b3Sopenharmony_ci goto fail; 40853a5a1b3Sopenharmony_ci 40953a5a1b3Sopenharmony_ci t = pa_tagstruct_new_fixed(data.data, data.size); 41053a5a1b3Sopenharmony_ci e = perportentry_new(false); 41153a5a1b3Sopenharmony_ci 41253a5a1b3Sopenharmony_ci if (pa_tagstruct_getu8(t, &e->version) < 0 || 41353a5a1b3Sopenharmony_ci e->version > PERPORTENTRY_VERSION || 41453a5a1b3Sopenharmony_ci pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 || 41553a5a1b3Sopenharmony_ci pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 || 41653a5a1b3Sopenharmony_ci pa_tagstruct_get_cvolume(t, &e->volume) < 0 || 41753a5a1b3Sopenharmony_ci pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 || 41853a5a1b3Sopenharmony_ci pa_tagstruct_get_boolean(t, &e->muted) < 0 || 41953a5a1b3Sopenharmony_ci pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) { 42053a5a1b3Sopenharmony_ci 42153a5a1b3Sopenharmony_ci goto fail; 42253a5a1b3Sopenharmony_ci } 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci for (i = 0; i < n_formats; ++i) { 42553a5a1b3Sopenharmony_ci pa_format_info *f = pa_format_info_new(); 42653a5a1b3Sopenharmony_ci if (pa_tagstruct_get_format_info(t, f) < 0) { 42753a5a1b3Sopenharmony_ci pa_format_info_free(f); 42853a5a1b3Sopenharmony_ci goto fail; 42953a5a1b3Sopenharmony_ci } 43053a5a1b3Sopenharmony_ci pa_idxset_put(e->formats, f, NULL); 43153a5a1b3Sopenharmony_ci } 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 43453a5a1b3Sopenharmony_ci goto fail; 43553a5a1b3Sopenharmony_ci 43653a5a1b3Sopenharmony_ci if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) { 43753a5a1b3Sopenharmony_ci pa_log_warn("Invalid channel map stored in database for device %s", name); 43853a5a1b3Sopenharmony_ci goto fail; 43953a5a1b3Sopenharmony_ci } 44053a5a1b3Sopenharmony_ci 44153a5a1b3Sopenharmony_ci if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) { 44253a5a1b3Sopenharmony_ci pa_log_warn("Volume and channel map don't match in database entry for device %s", name); 44353a5a1b3Sopenharmony_ci goto fail; 44453a5a1b3Sopenharmony_ci } 44553a5a1b3Sopenharmony_ci 44653a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 44753a5a1b3Sopenharmony_ci pa_datum_free(&data); 44853a5a1b3Sopenharmony_ci pa_xfree(name); 44953a5a1b3Sopenharmony_ci 45053a5a1b3Sopenharmony_ci return e; 45153a5a1b3Sopenharmony_ci 45253a5a1b3Sopenharmony_cifail: 45353a5a1b3Sopenharmony_ci 45453a5a1b3Sopenharmony_ci if (e) 45553a5a1b3Sopenharmony_ci perportentry_free(e); 45653a5a1b3Sopenharmony_ci if (t) 45753a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 45853a5a1b3Sopenharmony_ci 45953a5a1b3Sopenharmony_ci pa_datum_free(&data); 46053a5a1b3Sopenharmony_ci 46153a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 46253a5a1b3Sopenharmony_ci /* Try again with a null port. This is used when dealing with migration from older versions */ 46353a5a1b3Sopenharmony_ci if (port) { 46453a5a1b3Sopenharmony_ci pa_xfree(name); 46553a5a1b3Sopenharmony_ci return perportentry_read(u, basekeyname, NULL); 46653a5a1b3Sopenharmony_ci } 46753a5a1b3Sopenharmony_ci#endif 46853a5a1b3Sopenharmony_ci 46953a5a1b3Sopenharmony_ci pa_log_debug("Database contains no (or invalid) data for key: %s", name); 47053a5a1b3Sopenharmony_ci 47153a5a1b3Sopenharmony_ci pa_xfree(name); 47253a5a1b3Sopenharmony_ci 47353a5a1b3Sopenharmony_ci return NULL; 47453a5a1b3Sopenharmony_ci} 47553a5a1b3Sopenharmony_ci 47653a5a1b3Sopenharmony_cistatic struct perportentry* perportentry_copy(const struct perportentry *e) { 47753a5a1b3Sopenharmony_ci struct perportentry* r; 47853a5a1b3Sopenharmony_ci uint32_t idx; 47953a5a1b3Sopenharmony_ci pa_format_info *f; 48053a5a1b3Sopenharmony_ci 48153a5a1b3Sopenharmony_ci pa_assert(e); 48253a5a1b3Sopenharmony_ci r = perportentry_new(false); 48353a5a1b3Sopenharmony_ci r->version = e->version; 48453a5a1b3Sopenharmony_ci r->muted_valid = e->muted_valid; 48553a5a1b3Sopenharmony_ci r->volume_valid = e->volume_valid; 48653a5a1b3Sopenharmony_ci r->muted = e->muted; 48753a5a1b3Sopenharmony_ci r->channel_map = e->channel_map; 48853a5a1b3Sopenharmony_ci r->volume = e->volume; 48953a5a1b3Sopenharmony_ci 49053a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(f, e->formats, idx) { 49153a5a1b3Sopenharmony_ci pa_idxset_put(r->formats, pa_format_info_copy(f), NULL); 49253a5a1b3Sopenharmony_ci } 49353a5a1b3Sopenharmony_ci return r; 49453a5a1b3Sopenharmony_ci} 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_cistatic bool perportentries_equal(const struct perportentry *a, const struct perportentry *b) { 49753a5a1b3Sopenharmony_ci pa_cvolume t; 49853a5a1b3Sopenharmony_ci 49953a5a1b3Sopenharmony_ci pa_assert(a && b); 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci if (a->muted_valid != b->muted_valid || 50253a5a1b3Sopenharmony_ci (a->muted_valid && (a->muted != b->muted))) 50353a5a1b3Sopenharmony_ci return false; 50453a5a1b3Sopenharmony_ci 50553a5a1b3Sopenharmony_ci t = b->volume; 50653a5a1b3Sopenharmony_ci if (a->volume_valid != b->volume_valid || 50753a5a1b3Sopenharmony_ci (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume))) 50853a5a1b3Sopenharmony_ci return false; 50953a5a1b3Sopenharmony_ci 51053a5a1b3Sopenharmony_ci if (pa_idxset_size(a->formats) != pa_idxset_size(b->formats)) 51153a5a1b3Sopenharmony_ci return false; 51253a5a1b3Sopenharmony_ci 51353a5a1b3Sopenharmony_ci /** TODO: Compare a bit better */ 51453a5a1b3Sopenharmony_ci 51553a5a1b3Sopenharmony_ci return true; 51653a5a1b3Sopenharmony_ci} 51753a5a1b3Sopenharmony_ci 51853a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 51953a5a1b3Sopenharmony_ci 52053a5a1b3Sopenharmony_ci#define LEGACY_ENTRY_VERSION 2 52153a5a1b3Sopenharmony_cistatic bool legacy_entry_read(struct userdata *u, pa_datum *data, struct entry **entry, struct perportentry **perportentry) { 52253a5a1b3Sopenharmony_ci struct legacy_entry { 52353a5a1b3Sopenharmony_ci uint8_t version; 52453a5a1b3Sopenharmony_ci bool muted_valid:1, volume_valid:1, port_valid:1; 52553a5a1b3Sopenharmony_ci bool muted:1; 52653a5a1b3Sopenharmony_ci pa_channel_map channel_map; 52753a5a1b3Sopenharmony_ci pa_cvolume volume; 52853a5a1b3Sopenharmony_ci char port[PA_NAME_MAX]; 52953a5a1b3Sopenharmony_ci } PA_GCC_PACKED; 53053a5a1b3Sopenharmony_ci struct legacy_entry *le; 53153a5a1b3Sopenharmony_ci pa_channel_map channel_map; 53253a5a1b3Sopenharmony_ci pa_cvolume volume; 53353a5a1b3Sopenharmony_ci 53453a5a1b3Sopenharmony_ci pa_assert(u); 53553a5a1b3Sopenharmony_ci pa_assert(data); 53653a5a1b3Sopenharmony_ci pa_assert(entry); 53753a5a1b3Sopenharmony_ci pa_assert(perportentry); 53853a5a1b3Sopenharmony_ci 53953a5a1b3Sopenharmony_ci if (data->size != sizeof(struct legacy_entry)) { 54053a5a1b3Sopenharmony_ci pa_log_debug("Size does not match."); 54153a5a1b3Sopenharmony_ci return false; 54253a5a1b3Sopenharmony_ci } 54353a5a1b3Sopenharmony_ci 54453a5a1b3Sopenharmony_ci le = (struct legacy_entry*)data->data; 54553a5a1b3Sopenharmony_ci 54653a5a1b3Sopenharmony_ci if (le->version != LEGACY_ENTRY_VERSION) { 54753a5a1b3Sopenharmony_ci pa_log_debug("Version mismatch."); 54853a5a1b3Sopenharmony_ci return false; 54953a5a1b3Sopenharmony_ci } 55053a5a1b3Sopenharmony_ci 55153a5a1b3Sopenharmony_ci if (!memchr(le->port, 0, sizeof(le->port))) { 55253a5a1b3Sopenharmony_ci pa_log_warn("Port has missing NUL byte."); 55353a5a1b3Sopenharmony_ci return false; 55453a5a1b3Sopenharmony_ci } 55553a5a1b3Sopenharmony_ci 55653a5a1b3Sopenharmony_ci /* Read these out before accessing contents via pointers as struct legacy_entry may not be adequately aligned for these 55753a5a1b3Sopenharmony_ci * members to be accessed directly */ 55853a5a1b3Sopenharmony_ci channel_map = le->channel_map; 55953a5a1b3Sopenharmony_ci volume = le->volume; 56053a5a1b3Sopenharmony_ci 56153a5a1b3Sopenharmony_ci if (le->volume_valid && !pa_channel_map_valid(&channel_map)) { 56253a5a1b3Sopenharmony_ci pa_log_warn("Invalid channel map."); 56353a5a1b3Sopenharmony_ci return false; 56453a5a1b3Sopenharmony_ci } 56553a5a1b3Sopenharmony_ci 56653a5a1b3Sopenharmony_ci if (le->volume_valid && (!pa_cvolume_valid(&volume) || !pa_cvolume_compatible_with_channel_map(&volume, &channel_map))) { 56753a5a1b3Sopenharmony_ci pa_log_warn("Volume and channel map don't match."); 56853a5a1b3Sopenharmony_ci return false; 56953a5a1b3Sopenharmony_ci } 57053a5a1b3Sopenharmony_ci 57153a5a1b3Sopenharmony_ci *entry = entry_new(); 57253a5a1b3Sopenharmony_ci (*entry)->port_valid = le->port_valid; 57353a5a1b3Sopenharmony_ci (*entry)->port = pa_xstrdup(le->port); 57453a5a1b3Sopenharmony_ci 57553a5a1b3Sopenharmony_ci *perportentry = perportentry_new(true); 57653a5a1b3Sopenharmony_ci (*perportentry)->muted_valid = le->muted_valid; 57753a5a1b3Sopenharmony_ci (*perportentry)->volume_valid = le->volume_valid; 57853a5a1b3Sopenharmony_ci (*perportentry)->muted = le->muted; 57953a5a1b3Sopenharmony_ci (*perportentry)->channel_map = le->channel_map; 58053a5a1b3Sopenharmony_ci (*perportentry)->volume = le->volume; 58153a5a1b3Sopenharmony_ci 58253a5a1b3Sopenharmony_ci return true; 58353a5a1b3Sopenharmony_ci} 58453a5a1b3Sopenharmony_ci#endif 58553a5a1b3Sopenharmony_ci 58653a5a1b3Sopenharmony_cistatic void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { 58753a5a1b3Sopenharmony_ci struct userdata *u = userdata; 58853a5a1b3Sopenharmony_ci struct entry *e, *olde; 58953a5a1b3Sopenharmony_ci struct perportentry *ppe, *oldppe; 59053a5a1b3Sopenharmony_ci char *name; 59153a5a1b3Sopenharmony_ci const char *port = NULL; 59253a5a1b3Sopenharmony_ci pa_device_type_t type; 59353a5a1b3Sopenharmony_ci bool written = false; 59453a5a1b3Sopenharmony_ci 59553a5a1b3Sopenharmony_ci pa_assert(c); 59653a5a1b3Sopenharmony_ci pa_assert(u); 59753a5a1b3Sopenharmony_ci 59853a5a1b3Sopenharmony_ci if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && 59953a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && 60053a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && 60153a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)) 60253a5a1b3Sopenharmony_ci return; 60353a5a1b3Sopenharmony_ci 60453a5a1b3Sopenharmony_ci if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { 60553a5a1b3Sopenharmony_ci pa_sink *sink; 60653a5a1b3Sopenharmony_ci 60753a5a1b3Sopenharmony_ci if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) 60853a5a1b3Sopenharmony_ci return; 60953a5a1b3Sopenharmony_ci 61053a5a1b3Sopenharmony_ci type = PA_DEVICE_TYPE_SINK; 61153a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", sink->name); 61253a5a1b3Sopenharmony_ci if (sink->active_port) 61353a5a1b3Sopenharmony_ci port = sink->active_port->name; 61453a5a1b3Sopenharmony_ci 61553a5a1b3Sopenharmony_ci if ((olde = entry_read(u, name))) 61653a5a1b3Sopenharmony_ci e = entry_copy(olde); 61753a5a1b3Sopenharmony_ci else 61853a5a1b3Sopenharmony_ci e = entry_new(); 61953a5a1b3Sopenharmony_ci 62053a5a1b3Sopenharmony_ci if (sink->save_port) { 62153a5a1b3Sopenharmony_ci pa_xfree(e->port); 62253a5a1b3Sopenharmony_ci e->port = pa_xstrdup(port ? port : ""); 62353a5a1b3Sopenharmony_ci e->port_valid = true; 62453a5a1b3Sopenharmony_ci } 62553a5a1b3Sopenharmony_ci 62653a5a1b3Sopenharmony_ci if ((oldppe = perportentry_read(u, name, port))) 62753a5a1b3Sopenharmony_ci ppe = perportentry_copy(oldppe); 62853a5a1b3Sopenharmony_ci else 62953a5a1b3Sopenharmony_ci ppe = perportentry_new(true); 63053a5a1b3Sopenharmony_ci 63153a5a1b3Sopenharmony_ci if (sink->save_volume) { 63253a5a1b3Sopenharmony_ci ppe->channel_map = sink->channel_map; 63353a5a1b3Sopenharmony_ci ppe->volume = *pa_sink_get_volume(sink, false); 63453a5a1b3Sopenharmony_ci ppe->volume_valid = true; 63553a5a1b3Sopenharmony_ci } 63653a5a1b3Sopenharmony_ci 63753a5a1b3Sopenharmony_ci if (sink->save_muted) { 63853a5a1b3Sopenharmony_ci ppe->muted = pa_sink_get_mute(sink, false); 63953a5a1b3Sopenharmony_ci ppe->muted_valid = true; 64053a5a1b3Sopenharmony_ci } 64153a5a1b3Sopenharmony_ci } else { 64253a5a1b3Sopenharmony_ci pa_source *source; 64353a5a1b3Sopenharmony_ci 64453a5a1b3Sopenharmony_ci pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); 64553a5a1b3Sopenharmony_ci 64653a5a1b3Sopenharmony_ci if (!(source = pa_idxset_get_by_index(c->sources, idx))) 64753a5a1b3Sopenharmony_ci return; 64853a5a1b3Sopenharmony_ci 64953a5a1b3Sopenharmony_ci type = PA_DEVICE_TYPE_SOURCE; 65053a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("source:%s", source->name); 65153a5a1b3Sopenharmony_ci if (source->active_port) 65253a5a1b3Sopenharmony_ci port = source->active_port->name; 65353a5a1b3Sopenharmony_ci 65453a5a1b3Sopenharmony_ci if ((olde = entry_read(u, name))) 65553a5a1b3Sopenharmony_ci e = entry_copy(olde); 65653a5a1b3Sopenharmony_ci else 65753a5a1b3Sopenharmony_ci e = entry_new(); 65853a5a1b3Sopenharmony_ci 65953a5a1b3Sopenharmony_ci if (source->save_port) { 66053a5a1b3Sopenharmony_ci pa_xfree(e->port); 66153a5a1b3Sopenharmony_ci e->port = pa_xstrdup(port ? port : ""); 66253a5a1b3Sopenharmony_ci e->port_valid = true; 66353a5a1b3Sopenharmony_ci } 66453a5a1b3Sopenharmony_ci 66553a5a1b3Sopenharmony_ci if ((oldppe = perportentry_read(u, name, port))) 66653a5a1b3Sopenharmony_ci ppe = perportentry_copy(oldppe); 66753a5a1b3Sopenharmony_ci else 66853a5a1b3Sopenharmony_ci ppe = perportentry_new(true); 66953a5a1b3Sopenharmony_ci 67053a5a1b3Sopenharmony_ci if (source->save_volume) { 67153a5a1b3Sopenharmony_ci ppe->channel_map = source->channel_map; 67253a5a1b3Sopenharmony_ci ppe->volume = *pa_source_get_volume(source, false); 67353a5a1b3Sopenharmony_ci ppe->volume_valid = true; 67453a5a1b3Sopenharmony_ci } 67553a5a1b3Sopenharmony_ci 67653a5a1b3Sopenharmony_ci if (source->save_muted) { 67753a5a1b3Sopenharmony_ci ppe->muted = pa_source_get_mute(source, false); 67853a5a1b3Sopenharmony_ci ppe->muted_valid = true; 67953a5a1b3Sopenharmony_ci } 68053a5a1b3Sopenharmony_ci } 68153a5a1b3Sopenharmony_ci 68253a5a1b3Sopenharmony_ci pa_assert(e); 68353a5a1b3Sopenharmony_ci 68453a5a1b3Sopenharmony_ci if (olde) { 68553a5a1b3Sopenharmony_ci 68653a5a1b3Sopenharmony_ci if (entries_equal(olde, e)) { 68753a5a1b3Sopenharmony_ci entry_free(olde); 68853a5a1b3Sopenharmony_ci entry_free(e); 68953a5a1b3Sopenharmony_ci e = NULL; 69053a5a1b3Sopenharmony_ci } else 69153a5a1b3Sopenharmony_ci entry_free(olde); 69253a5a1b3Sopenharmony_ci } 69353a5a1b3Sopenharmony_ci 69453a5a1b3Sopenharmony_ci if (e) { 69553a5a1b3Sopenharmony_ci pa_log_info("Storing port for device %s.", name); 69653a5a1b3Sopenharmony_ci 69753a5a1b3Sopenharmony_ci written = entry_write(u, name, e); 69853a5a1b3Sopenharmony_ci 69953a5a1b3Sopenharmony_ci entry_free(e); 70053a5a1b3Sopenharmony_ci } 70153a5a1b3Sopenharmony_ci 70253a5a1b3Sopenharmony_ci pa_assert(ppe); 70353a5a1b3Sopenharmony_ci 70453a5a1b3Sopenharmony_ci if (oldppe) { 70553a5a1b3Sopenharmony_ci 70653a5a1b3Sopenharmony_ci if (perportentries_equal(oldppe, ppe)) { 70753a5a1b3Sopenharmony_ci perportentry_free(oldppe); 70853a5a1b3Sopenharmony_ci perportentry_free(ppe); 70953a5a1b3Sopenharmony_ci ppe = NULL; 71053a5a1b3Sopenharmony_ci } else 71153a5a1b3Sopenharmony_ci perportentry_free(oldppe); 71253a5a1b3Sopenharmony_ci } 71353a5a1b3Sopenharmony_ci 71453a5a1b3Sopenharmony_ci if (ppe) { 71553a5a1b3Sopenharmony_ci pa_log_info("Storing volume/mute for device+port %s:%s.", name, (port ? port : "null")); 71653a5a1b3Sopenharmony_ci 71753a5a1b3Sopenharmony_ci written = perportentry_write(u, name, port, ppe) || written; 71853a5a1b3Sopenharmony_ci 71953a5a1b3Sopenharmony_ci perportentry_free(ppe); 72053a5a1b3Sopenharmony_ci } 72153a5a1b3Sopenharmony_ci pa_xfree(name); 72253a5a1b3Sopenharmony_ci 72353a5a1b3Sopenharmony_ci if (written) 72453a5a1b3Sopenharmony_ci trigger_save(u, type, idx); 72553a5a1b3Sopenharmony_ci} 72653a5a1b3Sopenharmony_ci 72753a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { 72853a5a1b3Sopenharmony_ci char *name; 72953a5a1b3Sopenharmony_ci struct entry *e; 73053a5a1b3Sopenharmony_ci 73153a5a1b3Sopenharmony_ci pa_assert(c); 73253a5a1b3Sopenharmony_ci pa_assert(new_data); 73353a5a1b3Sopenharmony_ci pa_assert(u); 73453a5a1b3Sopenharmony_ci pa_assert(u->restore_port); 73553a5a1b3Sopenharmony_ci 73653a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", new_data->name); 73753a5a1b3Sopenharmony_ci 73853a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 73953a5a1b3Sopenharmony_ci 74053a5a1b3Sopenharmony_ci if (e->port_valid) { 74153a5a1b3Sopenharmony_ci if (!new_data->active_port) { 74253a5a1b3Sopenharmony_ci pa_log_info("Restoring port '%s' for sink %s.", pa_strnull(e->port), name); 74353a5a1b3Sopenharmony_ci pa_sink_new_data_set_port(new_data, e->port); 74453a5a1b3Sopenharmony_ci new_data->save_port = true; 74553a5a1b3Sopenharmony_ci } else 74653a5a1b3Sopenharmony_ci pa_log_debug("Not restoring port for sink %s, because already set.", name); 74753a5a1b3Sopenharmony_ci } 74853a5a1b3Sopenharmony_ci 74953a5a1b3Sopenharmony_ci entry_free(e); 75053a5a1b3Sopenharmony_ci } 75153a5a1b3Sopenharmony_ci 75253a5a1b3Sopenharmony_ci pa_xfree(name); 75353a5a1b3Sopenharmony_ci 75453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 75553a5a1b3Sopenharmony_ci} 75653a5a1b3Sopenharmony_ci 75753a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { 75853a5a1b3Sopenharmony_ci char *name; 75953a5a1b3Sopenharmony_ci struct perportentry *e; 76053a5a1b3Sopenharmony_ci 76153a5a1b3Sopenharmony_ci pa_assert(c); 76253a5a1b3Sopenharmony_ci pa_assert(new_data); 76353a5a1b3Sopenharmony_ci pa_assert(u); 76453a5a1b3Sopenharmony_ci pa_assert(u->restore_volume || u->restore_muted); 76553a5a1b3Sopenharmony_ci 76653a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", new_data->name); 76753a5a1b3Sopenharmony_ci 76853a5a1b3Sopenharmony_ci if ((e = perportentry_read(u, name, new_data->active_port))) { 76953a5a1b3Sopenharmony_ci 77053a5a1b3Sopenharmony_ci if (u->restore_volume && e->volume_valid) { 77153a5a1b3Sopenharmony_ci 77253a5a1b3Sopenharmony_ci if (!new_data->volume_is_set) { 77353a5a1b3Sopenharmony_ci pa_cvolume v; 77453a5a1b3Sopenharmony_ci char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; 77553a5a1b3Sopenharmony_ci 77653a5a1b3Sopenharmony_ci v = e->volume; 77753a5a1b3Sopenharmony_ci pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); 77853a5a1b3Sopenharmony_ci pa_sink_new_data_set_volume(new_data, &v); 77953a5a1b3Sopenharmony_ci pa_log_info("Restoring volume for sink %s: %s", new_data->name, 78053a5a1b3Sopenharmony_ci pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false)); 78153a5a1b3Sopenharmony_ci 78253a5a1b3Sopenharmony_ci new_data->save_volume = true; 78353a5a1b3Sopenharmony_ci } else 78453a5a1b3Sopenharmony_ci pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name); 78553a5a1b3Sopenharmony_ci } 78653a5a1b3Sopenharmony_ci 78753a5a1b3Sopenharmony_ci if (u->restore_muted && e->muted_valid) { 78853a5a1b3Sopenharmony_ci 78953a5a1b3Sopenharmony_ci if (!new_data->muted_is_set) { 79053a5a1b3Sopenharmony_ci pa_sink_new_data_set_muted(new_data, e->muted); 79153a5a1b3Sopenharmony_ci new_data->save_muted = true; 79253a5a1b3Sopenharmony_ci pa_log_info("Restoring mute state for sink %s: %smuted", new_data->name, 79353a5a1b3Sopenharmony_ci new_data->muted ? "" : "un"); 79453a5a1b3Sopenharmony_ci } else 79553a5a1b3Sopenharmony_ci pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name); 79653a5a1b3Sopenharmony_ci } 79753a5a1b3Sopenharmony_ci 79853a5a1b3Sopenharmony_ci perportentry_free(e); 79953a5a1b3Sopenharmony_ci } 80053a5a1b3Sopenharmony_ci 80153a5a1b3Sopenharmony_ci pa_xfree(name); 80253a5a1b3Sopenharmony_ci 80353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 80453a5a1b3Sopenharmony_ci} 80553a5a1b3Sopenharmony_ci 80653a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_port_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { 80753a5a1b3Sopenharmony_ci char *name; 80853a5a1b3Sopenharmony_ci struct perportentry *e; 80953a5a1b3Sopenharmony_ci 81053a5a1b3Sopenharmony_ci pa_assert(c); 81153a5a1b3Sopenharmony_ci pa_assert(sink); 81253a5a1b3Sopenharmony_ci pa_assert(u); 81353a5a1b3Sopenharmony_ci pa_assert(u->restore_volume || u->restore_muted); 81453a5a1b3Sopenharmony_ci 81553a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", sink->name); 81653a5a1b3Sopenharmony_ci 81753a5a1b3Sopenharmony_ci if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) { 81853a5a1b3Sopenharmony_ci 81953a5a1b3Sopenharmony_ci if (u->restore_volume && e->volume_valid) { 82053a5a1b3Sopenharmony_ci pa_cvolume v; 82153a5a1b3Sopenharmony_ci 82253a5a1b3Sopenharmony_ci pa_log_info("Restoring volume for sink %s.", sink->name); 82353a5a1b3Sopenharmony_ci v = e->volume; 82453a5a1b3Sopenharmony_ci pa_cvolume_remap(&v, &e->channel_map, &sink->channel_map); 82553a5a1b3Sopenharmony_ci pa_sink_set_volume(sink, &v, true, false); 82653a5a1b3Sopenharmony_ci 82753a5a1b3Sopenharmony_ci sink->save_volume = true; 82853a5a1b3Sopenharmony_ci } 82953a5a1b3Sopenharmony_ci 83053a5a1b3Sopenharmony_ci if (u->restore_muted && e->muted_valid) { 83153a5a1b3Sopenharmony_ci 83253a5a1b3Sopenharmony_ci pa_log_info("Restoring mute state for sink %s.", sink->name); 83353a5a1b3Sopenharmony_ci pa_sink_set_mute(sink, e->muted, false); 83453a5a1b3Sopenharmony_ci sink->save_muted = true; 83553a5a1b3Sopenharmony_ci } 83653a5a1b3Sopenharmony_ci 83753a5a1b3Sopenharmony_ci perportentry_free(e); 83853a5a1b3Sopenharmony_ci } 83953a5a1b3Sopenharmony_ci 84053a5a1b3Sopenharmony_ci pa_xfree(name); 84153a5a1b3Sopenharmony_ci 84253a5a1b3Sopenharmony_ci return PA_HOOK_OK; 84353a5a1b3Sopenharmony_ci} 84453a5a1b3Sopenharmony_ci 84553a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { 84653a5a1b3Sopenharmony_ci char *name; 84753a5a1b3Sopenharmony_ci struct perportentry *e; 84853a5a1b3Sopenharmony_ci 84953a5a1b3Sopenharmony_ci pa_assert(c); 85053a5a1b3Sopenharmony_ci pa_assert(sink); 85153a5a1b3Sopenharmony_ci pa_assert(u); 85253a5a1b3Sopenharmony_ci pa_assert(u->restore_formats); 85353a5a1b3Sopenharmony_ci 85453a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", sink->name); 85553a5a1b3Sopenharmony_ci 85653a5a1b3Sopenharmony_ci if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) { 85753a5a1b3Sopenharmony_ci 85853a5a1b3Sopenharmony_ci if (!pa_sink_set_formats(sink, e->formats)) 85953a5a1b3Sopenharmony_ci pa_log_debug("Could not set format on sink %s", sink->name); 86053a5a1b3Sopenharmony_ci 86153a5a1b3Sopenharmony_ci perportentry_free(e); 86253a5a1b3Sopenharmony_ci } 86353a5a1b3Sopenharmony_ci 86453a5a1b3Sopenharmony_ci pa_xfree(name); 86553a5a1b3Sopenharmony_ci 86653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 86753a5a1b3Sopenharmony_ci} 86853a5a1b3Sopenharmony_ci 86953a5a1b3Sopenharmony_cistatic pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { 87053a5a1b3Sopenharmony_ci char *name; 87153a5a1b3Sopenharmony_ci struct entry *e; 87253a5a1b3Sopenharmony_ci 87353a5a1b3Sopenharmony_ci pa_assert(c); 87453a5a1b3Sopenharmony_ci pa_assert(new_data); 87553a5a1b3Sopenharmony_ci pa_assert(u); 87653a5a1b3Sopenharmony_ci pa_assert(u->restore_port); 87753a5a1b3Sopenharmony_ci 87853a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("source:%s", new_data->name); 87953a5a1b3Sopenharmony_ci 88053a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 88153a5a1b3Sopenharmony_ci 88253a5a1b3Sopenharmony_ci if (e->port_valid) { 88353a5a1b3Sopenharmony_ci if (!new_data->active_port) { 88453a5a1b3Sopenharmony_ci pa_log_info("Restoring port '%s' for source %s.", pa_strnull(e->port), name); 88553a5a1b3Sopenharmony_ci pa_source_new_data_set_port(new_data, e->port); 88653a5a1b3Sopenharmony_ci new_data->save_port = true; 88753a5a1b3Sopenharmony_ci } else 88853a5a1b3Sopenharmony_ci pa_log_debug("Not restoring port for source %s, because already set.", name); 88953a5a1b3Sopenharmony_ci } 89053a5a1b3Sopenharmony_ci 89153a5a1b3Sopenharmony_ci entry_free(e); 89253a5a1b3Sopenharmony_ci } 89353a5a1b3Sopenharmony_ci 89453a5a1b3Sopenharmony_ci pa_xfree(name); 89553a5a1b3Sopenharmony_ci 89653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 89753a5a1b3Sopenharmony_ci} 89853a5a1b3Sopenharmony_ci 89953a5a1b3Sopenharmony_cistatic pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { 90053a5a1b3Sopenharmony_ci char *name; 90153a5a1b3Sopenharmony_ci struct perportentry *e; 90253a5a1b3Sopenharmony_ci 90353a5a1b3Sopenharmony_ci pa_assert(c); 90453a5a1b3Sopenharmony_ci pa_assert(new_data); 90553a5a1b3Sopenharmony_ci pa_assert(u); 90653a5a1b3Sopenharmony_ci pa_assert(u->restore_volume || u->restore_muted); 90753a5a1b3Sopenharmony_ci 90853a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("source:%s", new_data->name); 90953a5a1b3Sopenharmony_ci 91053a5a1b3Sopenharmony_ci if ((e = perportentry_read(u, name, new_data->active_port))) { 91153a5a1b3Sopenharmony_ci 91253a5a1b3Sopenharmony_ci if (u->restore_volume && e->volume_valid) { 91353a5a1b3Sopenharmony_ci 91453a5a1b3Sopenharmony_ci if (!new_data->volume_is_set) { 91553a5a1b3Sopenharmony_ci pa_cvolume v; 91653a5a1b3Sopenharmony_ci char buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; 91753a5a1b3Sopenharmony_ci 91853a5a1b3Sopenharmony_ci v = e->volume; 91953a5a1b3Sopenharmony_ci pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map); 92053a5a1b3Sopenharmony_ci pa_source_new_data_set_volume(new_data, &v); 92153a5a1b3Sopenharmony_ci pa_log_info("Restoring volume for source %s: %s", new_data->name, 92253a5a1b3Sopenharmony_ci pa_cvolume_snprint_verbose(buf, sizeof(buf), &new_data->volume, &new_data->channel_map, false)); 92353a5a1b3Sopenharmony_ci 92453a5a1b3Sopenharmony_ci new_data->save_volume = true; 92553a5a1b3Sopenharmony_ci } else 92653a5a1b3Sopenharmony_ci pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name); 92753a5a1b3Sopenharmony_ci } 92853a5a1b3Sopenharmony_ci 92953a5a1b3Sopenharmony_ci if (u->restore_muted && e->muted_valid) { 93053a5a1b3Sopenharmony_ci 93153a5a1b3Sopenharmony_ci if (!new_data->muted_is_set) { 93253a5a1b3Sopenharmony_ci pa_source_new_data_set_muted(new_data, e->muted); 93353a5a1b3Sopenharmony_ci new_data->save_muted = true; 93453a5a1b3Sopenharmony_ci pa_log_info("Restoring mute state for source %s: %smuted", new_data->name, 93553a5a1b3Sopenharmony_ci new_data->muted ? "" : "un"); 93653a5a1b3Sopenharmony_ci } else 93753a5a1b3Sopenharmony_ci pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name); 93853a5a1b3Sopenharmony_ci } 93953a5a1b3Sopenharmony_ci 94053a5a1b3Sopenharmony_ci perportentry_free(e); 94153a5a1b3Sopenharmony_ci } 94253a5a1b3Sopenharmony_ci 94353a5a1b3Sopenharmony_ci pa_xfree(name); 94453a5a1b3Sopenharmony_ci 94553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 94653a5a1b3Sopenharmony_ci} 94753a5a1b3Sopenharmony_ci 94853a5a1b3Sopenharmony_cistatic pa_hook_result_t source_port_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { 94953a5a1b3Sopenharmony_ci char *name; 95053a5a1b3Sopenharmony_ci struct perportentry *e; 95153a5a1b3Sopenharmony_ci 95253a5a1b3Sopenharmony_ci pa_assert(c); 95353a5a1b3Sopenharmony_ci pa_assert(source); 95453a5a1b3Sopenharmony_ci pa_assert(u); 95553a5a1b3Sopenharmony_ci pa_assert(u->restore_volume || u->restore_muted); 95653a5a1b3Sopenharmony_ci 95753a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("source:%s", source->name); 95853a5a1b3Sopenharmony_ci 95953a5a1b3Sopenharmony_ci if ((e = perportentry_read(u, name, (source->active_port ? source->active_port->name : NULL)))) { 96053a5a1b3Sopenharmony_ci 96153a5a1b3Sopenharmony_ci if (u->restore_volume && e->volume_valid) { 96253a5a1b3Sopenharmony_ci pa_cvolume v; 96353a5a1b3Sopenharmony_ci 96453a5a1b3Sopenharmony_ci pa_log_info("Restoring volume for source %s.", source->name); 96553a5a1b3Sopenharmony_ci v = e->volume; 96653a5a1b3Sopenharmony_ci pa_cvolume_remap(&v, &e->channel_map, &source->channel_map); 96753a5a1b3Sopenharmony_ci pa_source_set_volume(source, &v, true, false); 96853a5a1b3Sopenharmony_ci 96953a5a1b3Sopenharmony_ci source->save_volume = true; 97053a5a1b3Sopenharmony_ci } 97153a5a1b3Sopenharmony_ci 97253a5a1b3Sopenharmony_ci if (u->restore_muted && e->muted_valid) { 97353a5a1b3Sopenharmony_ci 97453a5a1b3Sopenharmony_ci pa_log_info("Restoring mute state for source %s.", source->name); 97553a5a1b3Sopenharmony_ci pa_source_set_mute(source, e->muted, false); 97653a5a1b3Sopenharmony_ci source->save_muted = true; 97753a5a1b3Sopenharmony_ci } 97853a5a1b3Sopenharmony_ci 97953a5a1b3Sopenharmony_ci perportentry_free(e); 98053a5a1b3Sopenharmony_ci } 98153a5a1b3Sopenharmony_ci 98253a5a1b3Sopenharmony_ci pa_xfree(name); 98353a5a1b3Sopenharmony_ci 98453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 98553a5a1b3Sopenharmony_ci} 98653a5a1b3Sopenharmony_ci 98753a5a1b3Sopenharmony_ci#define EXT_VERSION 1 98853a5a1b3Sopenharmony_ci 98953a5a1b3Sopenharmony_cistatic void read_sink_format_reply(struct userdata *u, pa_tagstruct *reply, pa_sink *sink) { 99053a5a1b3Sopenharmony_ci struct perportentry *e; 99153a5a1b3Sopenharmony_ci char *name; 99253a5a1b3Sopenharmony_ci 99353a5a1b3Sopenharmony_ci pa_assert(u); 99453a5a1b3Sopenharmony_ci pa_assert(reply); 99553a5a1b3Sopenharmony_ci pa_assert(sink); 99653a5a1b3Sopenharmony_ci 99753a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, PA_DEVICE_TYPE_SINK); 99853a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, sink->index); 99953a5a1b3Sopenharmony_ci 100053a5a1b3Sopenharmony_ci /* Read or create an entry */ 100153a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", sink->name); 100253a5a1b3Sopenharmony_ci if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) { 100353a5a1b3Sopenharmony_ci /* Fake a reply with PCM encoding supported */ 100453a5a1b3Sopenharmony_ci pa_format_info *f = pa_format_info_new(); 100553a5a1b3Sopenharmony_ci 100653a5a1b3Sopenharmony_ci pa_tagstruct_putu8(reply, 1); 100753a5a1b3Sopenharmony_ci f->encoding = PA_ENCODING_PCM; 100853a5a1b3Sopenharmony_ci pa_tagstruct_put_format_info(reply, f); 100953a5a1b3Sopenharmony_ci 101053a5a1b3Sopenharmony_ci pa_format_info_free(f); 101153a5a1b3Sopenharmony_ci } else { 101253a5a1b3Sopenharmony_ci uint32_t idx; 101353a5a1b3Sopenharmony_ci pa_format_info *f; 101453a5a1b3Sopenharmony_ci 101553a5a1b3Sopenharmony_ci /* Write all the formats from the entry to the reply */ 101653a5a1b3Sopenharmony_ci pa_tagstruct_putu8(reply, pa_idxset_size(e->formats)); 101753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(f, e->formats, idx) { 101853a5a1b3Sopenharmony_ci pa_tagstruct_put_format_info(reply, f); 101953a5a1b3Sopenharmony_ci } 102053a5a1b3Sopenharmony_ci perportentry_free(e); 102153a5a1b3Sopenharmony_ci } 102253a5a1b3Sopenharmony_ci pa_xfree(name); 102353a5a1b3Sopenharmony_ci} 102453a5a1b3Sopenharmony_ci 102553a5a1b3Sopenharmony_cistatic int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { 102653a5a1b3Sopenharmony_ci struct userdata *u; 102753a5a1b3Sopenharmony_ci uint32_t command; 102853a5a1b3Sopenharmony_ci pa_tagstruct *reply = NULL; 102953a5a1b3Sopenharmony_ci 103053a5a1b3Sopenharmony_ci pa_assert(p); 103153a5a1b3Sopenharmony_ci pa_assert(m); 103253a5a1b3Sopenharmony_ci pa_assert(c); 103353a5a1b3Sopenharmony_ci pa_assert(t); 103453a5a1b3Sopenharmony_ci 103553a5a1b3Sopenharmony_ci u = m->userdata; 103653a5a1b3Sopenharmony_ci 103753a5a1b3Sopenharmony_ci if (pa_tagstruct_getu32(t, &command) < 0) 103853a5a1b3Sopenharmony_ci goto fail; 103953a5a1b3Sopenharmony_ci 104053a5a1b3Sopenharmony_ci reply = pa_tagstruct_new(); 104153a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); 104253a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, tag); 104353a5a1b3Sopenharmony_ci 104453a5a1b3Sopenharmony_ci switch (command) { 104553a5a1b3Sopenharmony_ci case SUBCOMMAND_TEST: { 104653a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 104753a5a1b3Sopenharmony_ci goto fail; 104853a5a1b3Sopenharmony_ci 104953a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, EXT_VERSION); 105053a5a1b3Sopenharmony_ci break; 105153a5a1b3Sopenharmony_ci } 105253a5a1b3Sopenharmony_ci 105353a5a1b3Sopenharmony_ci case SUBCOMMAND_SUBSCRIBE: { 105453a5a1b3Sopenharmony_ci 105553a5a1b3Sopenharmony_ci bool enabled; 105653a5a1b3Sopenharmony_ci 105753a5a1b3Sopenharmony_ci if (pa_tagstruct_get_boolean(t, &enabled) < 0 || 105853a5a1b3Sopenharmony_ci !pa_tagstruct_eof(t)) 105953a5a1b3Sopenharmony_ci goto fail; 106053a5a1b3Sopenharmony_ci 106153a5a1b3Sopenharmony_ci if (enabled) 106253a5a1b3Sopenharmony_ci pa_idxset_put(u->subscribed, c, NULL); 106353a5a1b3Sopenharmony_ci else 106453a5a1b3Sopenharmony_ci pa_idxset_remove_by_data(u->subscribed, c, NULL); 106553a5a1b3Sopenharmony_ci 106653a5a1b3Sopenharmony_ci break; 106753a5a1b3Sopenharmony_ci } 106853a5a1b3Sopenharmony_ci 106953a5a1b3Sopenharmony_ci case SUBCOMMAND_READ_FORMATS_ALL: { 107053a5a1b3Sopenharmony_ci pa_sink *sink; 107153a5a1b3Sopenharmony_ci uint32_t idx; 107253a5a1b3Sopenharmony_ci 107353a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 107453a5a1b3Sopenharmony_ci goto fail; 107553a5a1b3Sopenharmony_ci 107653a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { 107753a5a1b3Sopenharmony_ci read_sink_format_reply(u, reply, sink); 107853a5a1b3Sopenharmony_ci } 107953a5a1b3Sopenharmony_ci 108053a5a1b3Sopenharmony_ci break; 108153a5a1b3Sopenharmony_ci } 108253a5a1b3Sopenharmony_ci case SUBCOMMAND_READ_FORMATS: { 108353a5a1b3Sopenharmony_ci pa_device_type_t type; 108453a5a1b3Sopenharmony_ci uint32_t sink_index; 108553a5a1b3Sopenharmony_ci pa_sink *sink; 108653a5a1b3Sopenharmony_ci 108753a5a1b3Sopenharmony_ci pa_assert(reply); 108853a5a1b3Sopenharmony_ci 108953a5a1b3Sopenharmony_ci /* Get the sink index and the number of formats from the tagstruct */ 109053a5a1b3Sopenharmony_ci if (pa_tagstruct_getu32(t, &type) < 0 || 109153a5a1b3Sopenharmony_ci pa_tagstruct_getu32(t, &sink_index) < 0) 109253a5a1b3Sopenharmony_ci goto fail; 109353a5a1b3Sopenharmony_ci 109453a5a1b3Sopenharmony_ci if (type != PA_DEVICE_TYPE_SINK) { 109553a5a1b3Sopenharmony_ci pa_log("Device format reading is only supported on sinks"); 109653a5a1b3Sopenharmony_ci goto fail; 109753a5a1b3Sopenharmony_ci } 109853a5a1b3Sopenharmony_ci 109953a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 110053a5a1b3Sopenharmony_ci goto fail; 110153a5a1b3Sopenharmony_ci 110253a5a1b3Sopenharmony_ci /* Now find our sink */ 110353a5a1b3Sopenharmony_ci if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index))) 110453a5a1b3Sopenharmony_ci goto fail; 110553a5a1b3Sopenharmony_ci 110653a5a1b3Sopenharmony_ci read_sink_format_reply(u, reply, sink); 110753a5a1b3Sopenharmony_ci 110853a5a1b3Sopenharmony_ci break; 110953a5a1b3Sopenharmony_ci } 111053a5a1b3Sopenharmony_ci 111153a5a1b3Sopenharmony_ci case SUBCOMMAND_SAVE_FORMATS: { 111253a5a1b3Sopenharmony_ci 111353a5a1b3Sopenharmony_ci struct perportentry *e; 111453a5a1b3Sopenharmony_ci pa_device_type_t type; 111553a5a1b3Sopenharmony_ci uint32_t sink_index; 111653a5a1b3Sopenharmony_ci char *name; 111753a5a1b3Sopenharmony_ci pa_sink *sink; 111853a5a1b3Sopenharmony_ci uint8_t i, n_formats; 111953a5a1b3Sopenharmony_ci 112053a5a1b3Sopenharmony_ci /* Get the sink index and the number of formats from the tagstruct */ 112153a5a1b3Sopenharmony_ci if (pa_tagstruct_getu32(t, &type) < 0 || 112253a5a1b3Sopenharmony_ci pa_tagstruct_getu32(t, &sink_index) < 0 || 112353a5a1b3Sopenharmony_ci pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) { 112453a5a1b3Sopenharmony_ci 112553a5a1b3Sopenharmony_ci goto fail; 112653a5a1b3Sopenharmony_ci } 112753a5a1b3Sopenharmony_ci 112853a5a1b3Sopenharmony_ci if (type != PA_DEVICE_TYPE_SINK) { 112953a5a1b3Sopenharmony_ci pa_log("Device format saving is only supported on sinks"); 113053a5a1b3Sopenharmony_ci goto fail; 113153a5a1b3Sopenharmony_ci } 113253a5a1b3Sopenharmony_ci 113353a5a1b3Sopenharmony_ci /* Now find our sink */ 113453a5a1b3Sopenharmony_ci if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index))) { 113553a5a1b3Sopenharmony_ci pa_log("Could not find sink #%d", sink_index); 113653a5a1b3Sopenharmony_ci goto fail; 113753a5a1b3Sopenharmony_ci } 113853a5a1b3Sopenharmony_ci 113953a5a1b3Sopenharmony_ci /* Read or create an entry */ 114053a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", sink->name); 114153a5a1b3Sopenharmony_ci if (!(e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) 114253a5a1b3Sopenharmony_ci e = perportentry_new(false); 114353a5a1b3Sopenharmony_ci else { 114453a5a1b3Sopenharmony_ci /* Clean out any saved formats */ 114553a5a1b3Sopenharmony_ci pa_idxset_free(e->formats, (pa_free_cb_t) pa_format_info_free); 114653a5a1b3Sopenharmony_ci e->formats = pa_idxset_new(NULL, NULL); 114753a5a1b3Sopenharmony_ci } 114853a5a1b3Sopenharmony_ci 114953a5a1b3Sopenharmony_ci /* Read all the formats from our tagstruct */ 115053a5a1b3Sopenharmony_ci for (i = 0; i < n_formats; ++i) { 115153a5a1b3Sopenharmony_ci pa_format_info *f = pa_format_info_new(); 115253a5a1b3Sopenharmony_ci if (pa_tagstruct_get_format_info(t, f) < 0) { 115353a5a1b3Sopenharmony_ci pa_format_info_free(f); 115453a5a1b3Sopenharmony_ci perportentry_free(e); 115553a5a1b3Sopenharmony_ci pa_xfree(name); 115653a5a1b3Sopenharmony_ci goto fail; 115753a5a1b3Sopenharmony_ci } 115853a5a1b3Sopenharmony_ci pa_idxset_put(e->formats, f, NULL); 115953a5a1b3Sopenharmony_ci } 116053a5a1b3Sopenharmony_ci 116153a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) { 116253a5a1b3Sopenharmony_ci perportentry_free(e); 116353a5a1b3Sopenharmony_ci pa_xfree(name); 116453a5a1b3Sopenharmony_ci goto fail; 116553a5a1b3Sopenharmony_ci } 116653a5a1b3Sopenharmony_ci 116753a5a1b3Sopenharmony_ci if (pa_sink_set_formats(sink, e->formats) && perportentry_write(u, name, (sink->active_port ? sink->active_port->name : NULL), e)) 116853a5a1b3Sopenharmony_ci trigger_save(u, type, sink_index); 116953a5a1b3Sopenharmony_ci else 117053a5a1b3Sopenharmony_ci pa_log_warn("Could not save format info for sink %s", sink->name); 117153a5a1b3Sopenharmony_ci 117253a5a1b3Sopenharmony_ci pa_xfree(name); 117353a5a1b3Sopenharmony_ci perportentry_free(e); 117453a5a1b3Sopenharmony_ci 117553a5a1b3Sopenharmony_ci break; 117653a5a1b3Sopenharmony_ci } 117753a5a1b3Sopenharmony_ci 117853a5a1b3Sopenharmony_ci default: 117953a5a1b3Sopenharmony_ci goto fail; 118053a5a1b3Sopenharmony_ci } 118153a5a1b3Sopenharmony_ci 118253a5a1b3Sopenharmony_ci pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply); 118353a5a1b3Sopenharmony_ci return 0; 118453a5a1b3Sopenharmony_ci 118553a5a1b3Sopenharmony_cifail: 118653a5a1b3Sopenharmony_ci 118753a5a1b3Sopenharmony_ci if (reply) 118853a5a1b3Sopenharmony_ci pa_tagstruct_free(reply); 118953a5a1b3Sopenharmony_ci 119053a5a1b3Sopenharmony_ci return -1; 119153a5a1b3Sopenharmony_ci} 119253a5a1b3Sopenharmony_ci 119353a5a1b3Sopenharmony_cistatic pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) { 119453a5a1b3Sopenharmony_ci pa_assert(p); 119553a5a1b3Sopenharmony_ci pa_assert(c); 119653a5a1b3Sopenharmony_ci pa_assert(u); 119753a5a1b3Sopenharmony_ci 119853a5a1b3Sopenharmony_ci pa_idxset_remove_by_data(u->subscribed, c, NULL); 119953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 120053a5a1b3Sopenharmony_ci} 120153a5a1b3Sopenharmony_ci 120253a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 120353a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 120453a5a1b3Sopenharmony_ci struct userdata *u; 120553a5a1b3Sopenharmony_ci char *state_path; 120653a5a1b3Sopenharmony_ci pa_sink *sink; 120753a5a1b3Sopenharmony_ci pa_source *source; 120853a5a1b3Sopenharmony_ci uint32_t idx; 120953a5a1b3Sopenharmony_ci bool restore_volume = true, restore_muted = true, restore_port = true, restore_formats = true; 121053a5a1b3Sopenharmony_ci 121153a5a1b3Sopenharmony_ci pa_assert(m); 121253a5a1b3Sopenharmony_ci 121353a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 121453a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments"); 121553a5a1b3Sopenharmony_ci goto fail; 121653a5a1b3Sopenharmony_ci } 121753a5a1b3Sopenharmony_ci 121853a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 || 121953a5a1b3Sopenharmony_ci pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 || 122053a5a1b3Sopenharmony_ci pa_modargs_get_value_boolean(ma, "restore_port", &restore_port) < 0 || 122153a5a1b3Sopenharmony_ci pa_modargs_get_value_boolean(ma, "restore_formats", &restore_formats) < 0) { 122253a5a1b3Sopenharmony_ci pa_log("restore_port, restore_volume, restore_muted and restore_formats expect boolean arguments"); 122353a5a1b3Sopenharmony_ci goto fail; 122453a5a1b3Sopenharmony_ci } 122553a5a1b3Sopenharmony_ci 122653a5a1b3Sopenharmony_ci if (!restore_muted && !restore_volume && !restore_port && !restore_formats) 122753a5a1b3Sopenharmony_ci pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring port enabled!"); 122853a5a1b3Sopenharmony_ci 122953a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 123053a5a1b3Sopenharmony_ci u->core = m->core; 123153a5a1b3Sopenharmony_ci u->module = m; 123253a5a1b3Sopenharmony_ci u->restore_volume = restore_volume; 123353a5a1b3Sopenharmony_ci u->restore_muted = restore_muted; 123453a5a1b3Sopenharmony_ci u->restore_port = restore_port; 123553a5a1b3Sopenharmony_ci u->restore_formats = restore_formats; 123653a5a1b3Sopenharmony_ci 123753a5a1b3Sopenharmony_ci u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 123853a5a1b3Sopenharmony_ci 123953a5a1b3Sopenharmony_ci u->protocol = pa_native_protocol_get(m->core); 124053a5a1b3Sopenharmony_ci pa_native_protocol_install_ext(u->protocol, m, extension_cb); 124153a5a1b3Sopenharmony_ci 124253a5a1b3Sopenharmony_ci 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); 124353a5a1b3Sopenharmony_ci 124453a5a1b3Sopenharmony_ci u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); 124553a5a1b3Sopenharmony_ci 124653a5a1b3Sopenharmony_ci if (restore_port) { 124753a5a1b3Sopenharmony_ci 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); 124853a5a1b3Sopenharmony_ci 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); 124953a5a1b3Sopenharmony_ci } 125053a5a1b3Sopenharmony_ci 125153a5a1b3Sopenharmony_ci if (restore_muted || restore_volume) { 125253a5a1b3Sopenharmony_ci 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); 125353a5a1b3Sopenharmony_ci 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); 125453a5a1b3Sopenharmony_ci 125553a5a1b3Sopenharmony_ci 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); 125653a5a1b3Sopenharmony_ci 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); 125753a5a1b3Sopenharmony_ci } 125853a5a1b3Sopenharmony_ci 125953a5a1b3Sopenharmony_ci if (restore_formats) 126053a5a1b3Sopenharmony_ci 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); 126153a5a1b3Sopenharmony_ci 126253a5a1b3Sopenharmony_ci if (!(state_path = pa_state_path(NULL, true))) 126353a5a1b3Sopenharmony_ci goto fail; 126453a5a1b3Sopenharmony_ci 126553a5a1b3Sopenharmony_ci if (!(u->database = pa_database_open(state_path, "device-volumes", true, true))) { 126653a5a1b3Sopenharmony_ci pa_xfree(state_path); 126753a5a1b3Sopenharmony_ci goto fail; 126853a5a1b3Sopenharmony_ci } 126953a5a1b3Sopenharmony_ci 127053a5a1b3Sopenharmony_ci pa_xfree(state_path); 127153a5a1b3Sopenharmony_ci 127253a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, m->core->sinks, idx) 127353a5a1b3Sopenharmony_ci subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u); 127453a5a1b3Sopenharmony_ci 127553a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, m->core->sources, idx) 127653a5a1b3Sopenharmony_ci subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); 127753a5a1b3Sopenharmony_ci 127853a5a1b3Sopenharmony_ci pa_modargs_free(ma); 127953a5a1b3Sopenharmony_ci return 0; 128053a5a1b3Sopenharmony_ci 128153a5a1b3Sopenharmony_cifail: 128253a5a1b3Sopenharmony_ci pa__done(m); 128353a5a1b3Sopenharmony_ci 128453a5a1b3Sopenharmony_ci if (ma) 128553a5a1b3Sopenharmony_ci pa_modargs_free(ma); 128653a5a1b3Sopenharmony_ci 128753a5a1b3Sopenharmony_ci return -1; 128853a5a1b3Sopenharmony_ci} 128953a5a1b3Sopenharmony_ci 129053a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 129153a5a1b3Sopenharmony_ci struct userdata* u; 129253a5a1b3Sopenharmony_ci 129353a5a1b3Sopenharmony_ci pa_assert(m); 129453a5a1b3Sopenharmony_ci 129553a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 129653a5a1b3Sopenharmony_ci return; 129753a5a1b3Sopenharmony_ci 129853a5a1b3Sopenharmony_ci if (u->subscription) 129953a5a1b3Sopenharmony_ci pa_subscription_free(u->subscription); 130053a5a1b3Sopenharmony_ci 130153a5a1b3Sopenharmony_ci if (u->save_time_event) { 130253a5a1b3Sopenharmony_ci u->core->mainloop->time_free(u->save_time_event); 130353a5a1b3Sopenharmony_ci pa_database_sync(u->database); 130453a5a1b3Sopenharmony_ci } 130553a5a1b3Sopenharmony_ci 130653a5a1b3Sopenharmony_ci if (u->database) 130753a5a1b3Sopenharmony_ci pa_database_close(u->database); 130853a5a1b3Sopenharmony_ci 130953a5a1b3Sopenharmony_ci if (u->protocol) { 131053a5a1b3Sopenharmony_ci pa_native_protocol_remove_ext(u->protocol, m); 131153a5a1b3Sopenharmony_ci pa_native_protocol_unref(u->protocol); 131253a5a1b3Sopenharmony_ci } 131353a5a1b3Sopenharmony_ci 131453a5a1b3Sopenharmony_ci if (u->subscribed) 131553a5a1b3Sopenharmony_ci pa_idxset_free(u->subscribed, NULL); 131653a5a1b3Sopenharmony_ci 131753a5a1b3Sopenharmony_ci pa_xfree(u); 131853a5a1b3Sopenharmony_ci} 1319