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