153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2006 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2553a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 2653a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <pulsecore/core.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h> 3153a5a1b3Sopenharmony_ci#include <pulsecore/source-output.h> 3253a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 3353a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering"); 3653a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it"); 3753a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 3853a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true); 3953a5a1b3Sopenharmony_ciPA_MODULE_USAGE("timeout=<timeout>"); 4053a5a1b3Sopenharmony_ci 4153a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 4253a5a1b3Sopenharmony_ci "timeout", 4353a5a1b3Sopenharmony_ci NULL, 4453a5a1b3Sopenharmony_ci}; 4553a5a1b3Sopenharmony_ci 4653a5a1b3Sopenharmony_cistruct userdata { 4753a5a1b3Sopenharmony_ci pa_core *core; 4853a5a1b3Sopenharmony_ci pa_usec_t timeout; 4953a5a1b3Sopenharmony_ci pa_hashmap *device_infos; 5053a5a1b3Sopenharmony_ci}; 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_cistruct device_info { 5353a5a1b3Sopenharmony_ci struct userdata *userdata; 5453a5a1b3Sopenharmony_ci pa_sink *sink; 5553a5a1b3Sopenharmony_ci pa_source *source; 5653a5a1b3Sopenharmony_ci pa_usec_t last_use; 5753a5a1b3Sopenharmony_ci pa_time_event *time_event; 5853a5a1b3Sopenharmony_ci pa_usec_t timeout; 5953a5a1b3Sopenharmony_ci}; 6053a5a1b3Sopenharmony_ci 6153a5a1b3Sopenharmony_cistatic void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { 6253a5a1b3Sopenharmony_ci struct device_info *d = userdata; 6353a5a1b3Sopenharmony_ci 6453a5a1b3Sopenharmony_ci pa_assert(d); 6553a5a1b3Sopenharmony_ci 6653a5a1b3Sopenharmony_ci d->userdata->core->mainloop->time_restart(d->time_event, NULL); 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci if (d->sink && pa_sink_check_suspend(d->sink, NULL, NULL) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) { 6953a5a1b3Sopenharmony_ci pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name); 7053a5a1b3Sopenharmony_ci pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE); 7153a5a1b3Sopenharmony_ci pa_core_maybe_vacuum(d->userdata->core); 7253a5a1b3Sopenharmony_ci } 7353a5a1b3Sopenharmony_ci 7453a5a1b3Sopenharmony_ci if (d->source && pa_source_check_suspend(d->source, NULL) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) { 7553a5a1b3Sopenharmony_ci pa_log_info("Source %s idle for too long, suspending ...", d->source->name); 7653a5a1b3Sopenharmony_ci pa_source_suspend(d->source, true, PA_SUSPEND_IDLE); 7753a5a1b3Sopenharmony_ci pa_core_maybe_vacuum(d->userdata->core); 7853a5a1b3Sopenharmony_ci } 7953a5a1b3Sopenharmony_ci} 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_cistatic void restart(struct device_info *d) { 8253a5a1b3Sopenharmony_ci pa_usec_t now; 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_ci pa_assert(d); 8553a5a1b3Sopenharmony_ci pa_assert(d->sink || d->source); 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci d->last_use = now = pa_rtclock_now(); 8853a5a1b3Sopenharmony_ci pa_core_rttime_restart(d->userdata->core, d->time_event, now + d->timeout); 8953a5a1b3Sopenharmony_ci 9053a5a1b3Sopenharmony_ci if (d->sink) 9153a5a1b3Sopenharmony_ci pa_log_debug("Sink %s becomes idle, timeout in %" PRIu64 " seconds.", d->sink->name, d->timeout / PA_USEC_PER_SEC); 9253a5a1b3Sopenharmony_ci if (d->source) 9353a5a1b3Sopenharmony_ci pa_log_debug("Source %s becomes idle, timeout in %" PRIu64 " seconds.", d->source->name, d->timeout / PA_USEC_PER_SEC); 9453a5a1b3Sopenharmony_ci} 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_cistatic void resume(struct device_info *d) { 9753a5a1b3Sopenharmony_ci pa_assert(d); 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci d->userdata->core->mainloop->time_restart(d->time_event, NULL); 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci if (d->sink) { 10253a5a1b3Sopenharmony_ci pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name); 10353a5a1b3Sopenharmony_ci pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE); 10453a5a1b3Sopenharmony_ci } 10553a5a1b3Sopenharmony_ci 10653a5a1b3Sopenharmony_ci if (d->source) { 10753a5a1b3Sopenharmony_ci pa_log_debug("Source %s becomes busy, resuming.", d->source->name); 10853a5a1b3Sopenharmony_ci pa_source_suspend(d->source, false, PA_SUSPEND_IDLE); 10953a5a1b3Sopenharmony_ci } 11053a5a1b3Sopenharmony_ci} 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) { 11353a5a1b3Sopenharmony_ci struct device_info *d; 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci pa_assert(c); 11653a5a1b3Sopenharmony_ci pa_assert(data); 11753a5a1b3Sopenharmony_ci pa_assert(u); 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci /* We need to resume the audio device here even for 12053a5a1b3Sopenharmony_ci * PA_SINK_INPUT_START_CORKED, since we need the device parameters 12153a5a1b3Sopenharmony_ci * to be fully available while the stream is set up. In that case, 12253a5a1b3Sopenharmony_ci * make sure we close the sink again after the timeout interval. */ 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci if ((d = pa_hashmap_get(u->device_infos, data->sink))) { 12553a5a1b3Sopenharmony_ci resume(d); 12653a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(d->sink, NULL, NULL) <= 0) 12753a5a1b3Sopenharmony_ci restart(d); 12853a5a1b3Sopenharmony_ci } 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 13153a5a1b3Sopenharmony_ci} 13253a5a1b3Sopenharmony_ci 13353a5a1b3Sopenharmony_cistatic pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_output_new_data *data, struct userdata *u) { 13453a5a1b3Sopenharmony_ci struct device_info *d; 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci pa_assert(c); 13753a5a1b3Sopenharmony_ci pa_assert(data); 13853a5a1b3Sopenharmony_ci pa_assert(u); 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci if (data->source->monitor_of) 14153a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, data->source->monitor_of); 14253a5a1b3Sopenharmony_ci else 14353a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, data->source); 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci if (d) { 14653a5a1b3Sopenharmony_ci resume(d); 14753a5a1b3Sopenharmony_ci if (d->source) { 14853a5a1b3Sopenharmony_ci if (pa_source_check_suspend(d->source, NULL) <= 0) 14953a5a1b3Sopenharmony_ci restart(d); 15053a5a1b3Sopenharmony_ci } else { 15153a5a1b3Sopenharmony_ci /* The source output is connected to a monitor source. */ 15253a5a1b3Sopenharmony_ci pa_assert(d->sink); 15353a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(d->sink, NULL, NULL) <= 0) 15453a5a1b3Sopenharmony_ci restart(d); 15553a5a1b3Sopenharmony_ci } 15653a5a1b3Sopenharmony_ci } 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 15953a5a1b3Sopenharmony_ci} 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { 16253a5a1b3Sopenharmony_ci pa_assert(c); 16353a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(s); 16453a5a1b3Sopenharmony_ci pa_assert(u); 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci if (!s->sink) 16753a5a1b3Sopenharmony_ci return PA_HOOK_OK; 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(s->sink, s, NULL) <= 0) { 17053a5a1b3Sopenharmony_ci struct device_info *d; 17153a5a1b3Sopenharmony_ci if ((d = pa_hashmap_get(u->device_infos, s->sink))) 17253a5a1b3Sopenharmony_ci restart(d); 17353a5a1b3Sopenharmony_ci } 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 17653a5a1b3Sopenharmony_ci} 17753a5a1b3Sopenharmony_ci 17853a5a1b3Sopenharmony_cistatic pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { 17953a5a1b3Sopenharmony_ci struct device_info *d = NULL; 18053a5a1b3Sopenharmony_ci 18153a5a1b3Sopenharmony_ci pa_assert(c); 18253a5a1b3Sopenharmony_ci pa_source_output_assert_ref(s); 18353a5a1b3Sopenharmony_ci pa_assert(u); 18453a5a1b3Sopenharmony_ci 18553a5a1b3Sopenharmony_ci if (!s->source) 18653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 18753a5a1b3Sopenharmony_ci 18853a5a1b3Sopenharmony_ci if (s->source->monitor_of) { 18953a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(s->source->monitor_of, NULL, s) <= 0) 19053a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source->monitor_of); 19153a5a1b3Sopenharmony_ci } else { 19253a5a1b3Sopenharmony_ci if (pa_source_check_suspend(s->source, s) <= 0) 19353a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source); 19453a5a1b3Sopenharmony_ci } 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci if (d) 19753a5a1b3Sopenharmony_ci restart(d); 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 20053a5a1b3Sopenharmony_ci} 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { 20353a5a1b3Sopenharmony_ci struct device_info *d; 20453a5a1b3Sopenharmony_ci 20553a5a1b3Sopenharmony_ci pa_assert(c); 20653a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(s); 20753a5a1b3Sopenharmony_ci pa_assert(u); 20853a5a1b3Sopenharmony_ci 20953a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(s->sink, s, NULL) <= 0) 21053a5a1b3Sopenharmony_ci if ((d = pa_hashmap_get(u->device_infos, s->sink))) 21153a5a1b3Sopenharmony_ci restart(d); 21253a5a1b3Sopenharmony_ci 21353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 21453a5a1b3Sopenharmony_ci} 21553a5a1b3Sopenharmony_ci 21653a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { 21753a5a1b3Sopenharmony_ci struct device_info *d; 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci pa_assert(c); 22053a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(s); 22153a5a1b3Sopenharmony_ci pa_assert(u); 22253a5a1b3Sopenharmony_ci 22353a5a1b3Sopenharmony_ci if (s->state != PA_SINK_INPUT_RUNNING) 22453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 22553a5a1b3Sopenharmony_ci 22653a5a1b3Sopenharmony_ci if ((d = pa_hashmap_get(u->device_infos, s->sink))) 22753a5a1b3Sopenharmony_ci resume(d); 22853a5a1b3Sopenharmony_ci 22953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 23053a5a1b3Sopenharmony_ci} 23153a5a1b3Sopenharmony_ci 23253a5a1b3Sopenharmony_cistatic pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { 23353a5a1b3Sopenharmony_ci struct device_info *d = NULL; 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ci pa_assert(c); 23653a5a1b3Sopenharmony_ci pa_source_output_assert_ref(s); 23753a5a1b3Sopenharmony_ci pa_assert(u); 23853a5a1b3Sopenharmony_ci 23953a5a1b3Sopenharmony_ci if (s->source->monitor_of) { 24053a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(s->source->monitor_of, NULL, s) <= 0) 24153a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source->monitor_of); 24253a5a1b3Sopenharmony_ci } else { 24353a5a1b3Sopenharmony_ci if (pa_source_check_suspend(s->source, s) <= 0) 24453a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source); 24553a5a1b3Sopenharmony_ci } 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci if (d) 24853a5a1b3Sopenharmony_ci restart(d); 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 25153a5a1b3Sopenharmony_ci} 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_cistatic pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { 25453a5a1b3Sopenharmony_ci struct device_info *d; 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci pa_assert(c); 25753a5a1b3Sopenharmony_ci pa_source_output_assert_ref(s); 25853a5a1b3Sopenharmony_ci pa_assert(u); 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci if (s->state != PA_SOURCE_OUTPUT_RUNNING) 26153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci if (s->source->monitor_of) 26453a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source->monitor_of); 26553a5a1b3Sopenharmony_ci else 26653a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source); 26753a5a1b3Sopenharmony_ci 26853a5a1b3Sopenharmony_ci if (d) 26953a5a1b3Sopenharmony_ci resume(d); 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 27253a5a1b3Sopenharmony_ci} 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) { 27553a5a1b3Sopenharmony_ci struct device_info *d; 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci pa_assert(c); 27853a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(s); 27953a5a1b3Sopenharmony_ci pa_assert(u); 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_ci if (s->state == PA_SINK_INPUT_RUNNING && s->sink) 28253a5a1b3Sopenharmony_ci if ((d = pa_hashmap_get(u->device_infos, s->sink))) 28353a5a1b3Sopenharmony_ci resume(d); 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 28653a5a1b3Sopenharmony_ci} 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_cistatic pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) { 28953a5a1b3Sopenharmony_ci pa_assert(c); 29053a5a1b3Sopenharmony_ci pa_source_output_assert_ref(s); 29153a5a1b3Sopenharmony_ci pa_assert(u); 29253a5a1b3Sopenharmony_ci 29353a5a1b3Sopenharmony_ci if (s->state == PA_SOURCE_OUTPUT_RUNNING && s->source) { 29453a5a1b3Sopenharmony_ci struct device_info *d; 29553a5a1b3Sopenharmony_ci 29653a5a1b3Sopenharmony_ci if (s->source->monitor_of) 29753a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source->monitor_of); 29853a5a1b3Sopenharmony_ci else 29953a5a1b3Sopenharmony_ci d = pa_hashmap_get(u->device_infos, s->source); 30053a5a1b3Sopenharmony_ci 30153a5a1b3Sopenharmony_ci if (d) 30253a5a1b3Sopenharmony_ci resume(d); 30353a5a1b3Sopenharmony_ci } 30453a5a1b3Sopenharmony_ci 30553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 30653a5a1b3Sopenharmony_ci} 30753a5a1b3Sopenharmony_ci 30853a5a1b3Sopenharmony_cistatic pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { 30953a5a1b3Sopenharmony_ci struct device_info *d; 31053a5a1b3Sopenharmony_ci pa_source *source; 31153a5a1b3Sopenharmony_ci pa_sink *sink; 31253a5a1b3Sopenharmony_ci const char *timeout_str; 31353a5a1b3Sopenharmony_ci int32_t timeout; 31453a5a1b3Sopenharmony_ci bool timeout_valid; 31553a5a1b3Sopenharmony_ci 31653a5a1b3Sopenharmony_ci pa_assert(c); 31753a5a1b3Sopenharmony_ci pa_object_assert_ref(o); 31853a5a1b3Sopenharmony_ci pa_assert(u); 31953a5a1b3Sopenharmony_ci 32053a5a1b3Sopenharmony_ci source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL; 32153a5a1b3Sopenharmony_ci sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL; 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci /* Never suspend monitors */ 32453a5a1b3Sopenharmony_ci if (source && source->monitor_of) 32553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 32653a5a1b3Sopenharmony_ci 32753a5a1b3Sopenharmony_ci pa_assert(source || sink); 32853a5a1b3Sopenharmony_ci 32953a5a1b3Sopenharmony_ci timeout_str = pa_proplist_gets(sink ? sink->proplist : source->proplist, "module-suspend-on-idle.timeout"); 33053a5a1b3Sopenharmony_ci if (timeout_str && pa_atoi(timeout_str, &timeout) >= 0) 33153a5a1b3Sopenharmony_ci timeout_valid = true; 33253a5a1b3Sopenharmony_ci else 33353a5a1b3Sopenharmony_ci timeout_valid = false; 33453a5a1b3Sopenharmony_ci 33553a5a1b3Sopenharmony_ci if (timeout_valid && timeout < 0) 33653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 33753a5a1b3Sopenharmony_ci 33853a5a1b3Sopenharmony_ci d = pa_xnew(struct device_info, 1); 33953a5a1b3Sopenharmony_ci d->userdata = u; 34053a5a1b3Sopenharmony_ci d->source = source ? pa_source_ref(source) : NULL; 34153a5a1b3Sopenharmony_ci d->sink = sink ? pa_sink_ref(sink) : NULL; 34253a5a1b3Sopenharmony_ci d->time_event = pa_core_rttime_new(c, PA_USEC_INVALID, timeout_cb, d); 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci if (timeout_valid) 34553a5a1b3Sopenharmony_ci d->timeout = timeout * PA_USEC_PER_SEC; 34653a5a1b3Sopenharmony_ci else 34753a5a1b3Sopenharmony_ci d->timeout = d->userdata->timeout; 34853a5a1b3Sopenharmony_ci 34953a5a1b3Sopenharmony_ci pa_hashmap_put(u->device_infos, o, d); 35053a5a1b3Sopenharmony_ci 35153a5a1b3Sopenharmony_ci if ((d->sink && pa_sink_check_suspend(d->sink, NULL, NULL) <= 0) || 35253a5a1b3Sopenharmony_ci (d->source && pa_source_check_suspend(d->source, NULL) <= 0)) 35353a5a1b3Sopenharmony_ci restart(d); 35453a5a1b3Sopenharmony_ci 35553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 35653a5a1b3Sopenharmony_ci} 35753a5a1b3Sopenharmony_ci 35853a5a1b3Sopenharmony_cistatic void device_info_free(struct device_info *d) { 35953a5a1b3Sopenharmony_ci pa_assert(d); 36053a5a1b3Sopenharmony_ci 36153a5a1b3Sopenharmony_ci if (d->source) 36253a5a1b3Sopenharmony_ci pa_source_unref(d->source); 36353a5a1b3Sopenharmony_ci if (d->sink) 36453a5a1b3Sopenharmony_ci pa_sink_unref(d->sink); 36553a5a1b3Sopenharmony_ci 36653a5a1b3Sopenharmony_ci d->userdata->core->mainloop->time_free(d->time_event); 36753a5a1b3Sopenharmony_ci 36853a5a1b3Sopenharmony_ci pa_xfree(d); 36953a5a1b3Sopenharmony_ci} 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_cistatic pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { 37253a5a1b3Sopenharmony_ci pa_assert(c); 37353a5a1b3Sopenharmony_ci pa_object_assert_ref(o); 37453a5a1b3Sopenharmony_ci pa_assert(u); 37553a5a1b3Sopenharmony_ci 37653a5a1b3Sopenharmony_ci pa_hashmap_remove_and_free(u->device_infos, o); 37753a5a1b3Sopenharmony_ci 37853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 37953a5a1b3Sopenharmony_ci} 38053a5a1b3Sopenharmony_ci 38153a5a1b3Sopenharmony_cistatic pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { 38253a5a1b3Sopenharmony_ci struct device_info *d; 38353a5a1b3Sopenharmony_ci 38453a5a1b3Sopenharmony_ci pa_assert(c); 38553a5a1b3Sopenharmony_ci pa_object_assert_ref(o); 38653a5a1b3Sopenharmony_ci pa_assert(u); 38753a5a1b3Sopenharmony_ci 38853a5a1b3Sopenharmony_ci if (!(d = pa_hashmap_get(u->device_infos, o))) { 38953a5a1b3Sopenharmony_ci /* We never suspend monitor sources, therefore they are not in the map. 39053a5a1b3Sopenharmony_ci * Still, when monitor source becomes idle it may happen that monitored 39153a5a1b3Sopenharmony_ci * sink has no uncorked inputs anymore and can now be suspended. 39253a5a1b3Sopenharmony_ci */ 39353a5a1b3Sopenharmony_ci if (pa_source_isinstance(o) && PA_SOURCE(o)->monitor_of) { 39453a5a1b3Sopenharmony_ci pa_log_debug("State of monitor source '%s' has changed, checking state of monitored sink", PA_SOURCE(o)->name); 39553a5a1b3Sopenharmony_ci return device_state_changed_hook_cb(c, PA_OBJECT(PA_SOURCE(o)->monitor_of), u); 39653a5a1b3Sopenharmony_ci } else 39753a5a1b3Sopenharmony_ci return PA_HOOK_OK; 39853a5a1b3Sopenharmony_ci } 39953a5a1b3Sopenharmony_ci 40053a5a1b3Sopenharmony_ci if (pa_sink_isinstance(o)) { 40153a5a1b3Sopenharmony_ci pa_sink *s = PA_SINK(o); 40253a5a1b3Sopenharmony_ci 40353a5a1b3Sopenharmony_ci if (pa_sink_check_suspend(s, NULL, NULL) <= 0) 40453a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(s->state)) 40553a5a1b3Sopenharmony_ci restart(d); 40653a5a1b3Sopenharmony_ci 40753a5a1b3Sopenharmony_ci } else if (pa_source_isinstance(o)) { 40853a5a1b3Sopenharmony_ci pa_source *s = PA_SOURCE(o); 40953a5a1b3Sopenharmony_ci 41053a5a1b3Sopenharmony_ci if (pa_source_check_suspend(s, NULL) <= 0) 41153a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_OPENED(s->state)) 41253a5a1b3Sopenharmony_ci restart(d); 41353a5a1b3Sopenharmony_ci } 41453a5a1b3Sopenharmony_ci 41553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 41653a5a1b3Sopenharmony_ci} 41753a5a1b3Sopenharmony_ci 41853a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 41953a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 42053a5a1b3Sopenharmony_ci struct userdata *u; 42153a5a1b3Sopenharmony_ci uint32_t timeout = 5; 42253a5a1b3Sopenharmony_ci uint32_t idx; 42353a5a1b3Sopenharmony_ci pa_sink *sink; 42453a5a1b3Sopenharmony_ci pa_source *source; 42553a5a1b3Sopenharmony_ci 42653a5a1b3Sopenharmony_ci pa_assert(m); 42753a5a1b3Sopenharmony_ci 42853a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 42953a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 43053a5a1b3Sopenharmony_ci goto fail; 43153a5a1b3Sopenharmony_ci } 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, "timeout", &timeout) < 0) { 43453a5a1b3Sopenharmony_ci pa_log("Failed to parse timeout value."); 43553a5a1b3Sopenharmony_ci goto fail; 43653a5a1b3Sopenharmony_ci } 43753a5a1b3Sopenharmony_ci 43853a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew(struct userdata, 1); 43953a5a1b3Sopenharmony_ci u->core = m->core; 44053a5a1b3Sopenharmony_ci u->timeout = timeout * PA_USEC_PER_SEC; 44153a5a1b3Sopenharmony_ci u->device_infos = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_info_free); 44253a5a1b3Sopenharmony_ci 44353a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, m->core->sinks, idx) 44453a5a1b3Sopenharmony_ci device_new_hook_cb(m->core, PA_OBJECT(sink), u); 44553a5a1b3Sopenharmony_ci 44653a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, m->core->sources, idx) 44753a5a1b3Sopenharmony_ci device_new_hook_cb(m->core, PA_OBJECT(source), u); 44853a5a1b3Sopenharmony_ci 44953a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u); 45053a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u); 45153a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u); 45253a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u); 45353a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u); 45453a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u); 45553a5a1b3Sopenharmony_ci 45653a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u); 45753a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u); 45853a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u); 45953a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u); 46053a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u); 46153a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u); 46253a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u); 46353a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u); 46453a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u); 46553a5a1b3Sopenharmony_ci pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u); 46653a5a1b3Sopenharmony_ci 46753a5a1b3Sopenharmony_ci pa_modargs_free(ma); 46853a5a1b3Sopenharmony_ci return 0; 46953a5a1b3Sopenharmony_ci 47053a5a1b3Sopenharmony_cifail: 47153a5a1b3Sopenharmony_ci 47253a5a1b3Sopenharmony_ci if (ma) 47353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 47453a5a1b3Sopenharmony_ci 47553a5a1b3Sopenharmony_ci return -1; 47653a5a1b3Sopenharmony_ci} 47753a5a1b3Sopenharmony_ci 47853a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 47953a5a1b3Sopenharmony_ci struct userdata *u; 48053a5a1b3Sopenharmony_ci struct device_info *d; 48153a5a1b3Sopenharmony_ci void *state; 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci pa_assert(m); 48453a5a1b3Sopenharmony_ci 48553a5a1b3Sopenharmony_ci if (!m->userdata) 48653a5a1b3Sopenharmony_ci return; 48753a5a1b3Sopenharmony_ci 48853a5a1b3Sopenharmony_ci u = m->userdata; 48953a5a1b3Sopenharmony_ci 49053a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(d, u->device_infos, state) { 49153a5a1b3Sopenharmony_ci if (d->sink && d->sink->state == PA_SINK_SUSPENDED) { 49253a5a1b3Sopenharmony_ci pa_log_debug("Resuming sink %s on module unload.", d->sink->name); 49353a5a1b3Sopenharmony_ci pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE); 49453a5a1b3Sopenharmony_ci } 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci if (d->source && d->source->state == PA_SOURCE_SUSPENDED) { 49753a5a1b3Sopenharmony_ci pa_log_debug("Resuming source %s on module unload.", d->source->name); 49853a5a1b3Sopenharmony_ci pa_source_suspend(d->source, false, PA_SUSPEND_IDLE); 49953a5a1b3Sopenharmony_ci } 50053a5a1b3Sopenharmony_ci } 50153a5a1b3Sopenharmony_ci 50253a5a1b3Sopenharmony_ci pa_hashmap_free(u->device_infos); 50353a5a1b3Sopenharmony_ci 50453a5a1b3Sopenharmony_ci pa_xfree(u); 50553a5a1b3Sopenharmony_ci} 506