]> code.delx.au - pulseaudio/blobdiff - src/modules/module-suspend-on-idle.c
Remove pa_bool_t and replace it with bool.
[pulseaudio] / src / modules / module-suspend-on-idle.c
index 5e5e53e79d56b4095eb67348655706b20fee43e4..6d49acb31f275967373205f19cf711b53aaa0f05 100644 (file)
@@ -5,7 +5,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
+#include <pulse/rtclock.h>
 
 #include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/namereg.h>
 
 #include "module-suspend-on-idle-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
 PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE("timeout=<timeout>");
 
 static const char* const valid_modargs[] = {
     "timeout",
@@ -74,41 +76,50 @@ struct device_info {
     struct userdata *userdata;
     pa_sink *sink;
     pa_source *source;
-    struct timeval last_use;
+    pa_usec_t last_use;
     pa_time_event *time_event;
 };
 
-static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
     struct device_info *d = userdata;
 
     pa_assert(d);
 
     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
 
-    if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
+    if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) {
         pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
-        pa_sink_suspend(d->sink, TRUE);
+        pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE);
+        pa_core_maybe_vacuum(d->userdata->core);
     }
 
-    if (d->source && pa_source_check_suspend(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
+    if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) {
         pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
-        pa_source_suspend(d->source, TRUE);
+        pa_source_suspend(d->source, true, PA_SUSPEND_IDLE);
+        pa_core_maybe_vacuum(d->userdata->core);
     }
 }
 
 static void restart(struct device_info *d) {
-    struct timeval tv;
+    pa_usec_t now;
+    const char *s;
+    uint32_t timeout;
+
     pa_assert(d);
+    pa_assert(d->sink || d->source);
+
+    d->last_use = now = pa_rtclock_now();
+
+    s = pa_proplist_gets(d->sink ? d->sink->proplist : d->source->proplist, "module-suspend-on-idle.timeout");
+    if (!s || pa_atou(s, &timeout) < 0)
+        timeout = d->userdata->timeout;
 
-    pa_gettimeofday(&tv);
-    d->last_use = tv;
-    pa_timeval_add(&tv, d->userdata->timeout*1000000);
-    d->userdata->core->mainloop->time_restart(d->time_event, &tv);
+    pa_core_rttime_restart(d->userdata->core, d->time_event, now + timeout * PA_USEC_PER_SEC);
 
     if (d->sink)
-        pa_log_debug("Sink %s becomes idle.", d->sink->name);
+        pa_log_debug("Sink %s becomes idle, timeout in %u seconds.", d->sink->name, timeout);
     if (d->source)
-        pa_log_debug("Source %s becomes idle.", d->source->name);
+        pa_log_debug("Source %s becomes idle, timeout in %u seconds.", d->source->name, timeout);
 }
 
 static void resume(struct device_info *d) {
@@ -117,15 +128,13 @@ static void resume(struct device_info *d) {
     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
 
     if (d->sink) {
-        pa_sink_suspend(d->sink, FALSE);
-
-        pa_log_debug("Sink %s becomes busy.", d->sink->name);
+        pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name);
+        pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE);
     }
 
     if (d->source) {
-        pa_source_suspend(d->source, FALSE);
-
-        pa_log_debug("Source %s becomes busy.", d->source->name);
+        pa_log_debug("Source %s becomes busy, resuming.", d->source->name);
+        pa_source_suspend(d->source, false, PA_SUSPEND_IDLE);
     }
 }
 
@@ -136,6 +145,10 @@ static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_
     pa_assert(data);
     pa_assert(u);
 
+    /* We need to resume the audio device here even for
+     * PA_SINK_INPUT_START_CORKED, since we need the device parameters
+     * to be fully available while the stream is set up. */
+
     if ((d = pa_hashmap_get(u->device_infos, data->sink)))
         resume(d);
 
@@ -149,7 +162,12 @@ static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_outpu
     pa_assert(data);
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, data->source)))
+    if (data->source->monitor_of)
+        d = pa_hashmap_get(u->device_infos, data->source->monitor_of);
+    else
+        d = pa_hashmap_get(u->device_infos, data->source);
+
+    if (d)
         resume(d);
 
     return PA_HOOK_OK;
@@ -160,6 +178,9 @@ static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s,
     pa_sink_input_assert_ref(s);
     pa_assert(u);
 
+    if (!s->sink)
+        return PA_HOOK_OK;
+
     if (pa_sink_check_suspend(s->sink) <= 0) {
         struct device_info *d;
         if ((d = pa_hashmap_get(u->device_infos, s->sink)))
@@ -170,16 +191,26 @@ static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s,
 }
 
 static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d = NULL;
+
     pa_assert(c);
     pa_source_output_assert_ref(s);
     pa_assert(u);
 
-    if (pa_source_check_suspend(s->source) <= 0) {
-        struct device_info *d;
-        if ((d = pa_hashmap_get(u->device_infos, s->source)))
-            restart(d);
+    if (!s->source)
+        return PA_HOOK_OK;
+
+    if (s->source->monitor_of) {
+        if (pa_sink_check_suspend(s->source->monitor_of) <= 0)
+            d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+    } else {
+        if (pa_source_check_suspend(s->source) <= 0)
+            d = pa_hashmap_get(u->device_infos, s->source);
     }
 
+    if (d)
+        restart(d);
+
     return PA_HOOK_OK;
 }
 
@@ -199,11 +230,16 @@ static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input
 
 static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
     struct device_info *d;
+    pa_sink_input_state_t state;
 
     pa_assert(c);
     pa_sink_input_assert_ref(s);
     pa_assert(u);
 
+    state = pa_sink_input_get_state(s);
+    if (state != PA_SINK_INPUT_RUNNING && state != PA_SINK_INPUT_DRAINED)
+        return PA_HOOK_OK;
+
     if ((d = pa_hashmap_get(u->device_infos, s->sink)))
         resume(d);
 
@@ -211,15 +247,22 @@ static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input
 }
 
 static pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
-    struct device_info *d;
+    struct device_info *d = NULL;
 
     pa_assert(c);
     pa_source_output_assert_ref(s);
     pa_assert(u);
 
-    if (pa_source_check_suspend(s->source) <= 1)
-        if ((d = pa_hashmap_get(u->device_infos, s->source)))
-            restart(d);
+    if (s->source->monitor_of) {
+        if (pa_sink_check_suspend(s->source->monitor_of) <= 1)
+            d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+    } else {
+        if (pa_source_check_suspend(s->source) <= 1)
+            d = pa_hashmap_get(u->device_infos, s->source);
+    }
+
+    if (d)
+        restart(d);
 
     return PA_HOOK_OK;
 }
@@ -231,7 +274,15 @@ static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_
     pa_source_output_assert_ref(s);
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, s->source)))
+    if (pa_source_output_get_state(s) != PA_SOURCE_OUTPUT_RUNNING)
+        return PA_HOOK_OK;
+
+    if (s->source->monitor_of)
+        d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+    else
+        d = pa_hashmap_get(u->device_infos, s->source);
+
+    if (d)
         resume(d);
 
     return PA_HOOK_OK;
@@ -240,6 +291,7 @@ static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_
 static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
     struct device_info *d;
     pa_sink_input_state_t state;
+
     pa_assert(c);
     pa_sink_input_assert_ref(s);
     pa_assert(u);
@@ -253,16 +305,21 @@ static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_inp
 }
 
 static pa_hook_result_t source_output_state_changed_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
-    struct device_info *d;
-    pa_source_output_state_t state;
     pa_assert(c);
     pa_source_output_assert_ref(s);
     pa_assert(u);
 
-    state = pa_source_output_get_state(s);
-    if (state == PA_SOURCE_OUTPUT_RUNNING)
-        if ((d = pa_hashmap_get(u->device_infos, s->source)))
+    if (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_RUNNING) {
+        struct device_info *d;
+
+        if (s->source->monitor_of)
+            d = pa_hashmap_get(u->device_infos, s->source->monitor_of);
+        else
+            d = pa_hashmap_get(u->device_infos, s->source);
+
+        if (d)
             resume(d);
+    }
 
     return PA_HOOK_OK;
 }
@@ -279,13 +336,17 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user
     source = pa_source_isinstance(o) ? PA_SOURCE(o) : NULL;
     sink = pa_sink_isinstance(o) ? PA_SINK(o) : NULL;
 
+    /* Never suspend monitors */
+    if (source && source->monitor_of)
+        return PA_HOOK_OK;
+
     pa_assert(source || sink);
 
     d = pa_xnew(struct device_info, 1);
     d->userdata = u;
     d->source = source ? pa_source_ref(source) : NULL;
     d->sink = sink ? pa_sink_ref(sink) : NULL;
-    d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
+    d->time_event = pa_core_rttime_new(c, PA_USEC_INVALID, timeout_cb, d);
     pa_hashmap_put(u->device_infos, o, d);
 
     if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) ||
@@ -335,22 +396,17 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
         pa_sink *s = PA_SINK(o);
         pa_sink_state_t state = pa_sink_get_state(s);
 
-        if (pa_sink_check_suspend(s) <= 0) {
-
+        if (pa_sink_check_suspend(s) <= 0)
             if (PA_SINK_IS_OPENED(state))
                 restart(d);
 
-        }
-
     } else if (pa_source_isinstance(o)) {
         pa_source *s = PA_SOURCE(o);
         pa_source_state_t state = pa_source_get_state(s);
 
-        if (pa_source_check_suspend(s) <= 0) {
-
+        if (pa_source_check_suspend(s) <= 0)
             if (PA_SOURCE_IS_OPENED(state))
                 restart(d);
-        }
     }
 
     return PA_HOOK_OK;
@@ -381,10 +437,10 @@ int pa__init(pa_module*m) {
     u->timeout = timeout;
     u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
-    for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+    PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
         device_new_hook_cb(m->core, PA_OBJECT(sink), u);
 
-    for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+    PA_IDXSET_FOREACH(source, m->core->sources, idx)
         device_new_hook_cb(m->core, PA_OBJECT(source), u);
 
     u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
@@ -418,7 +474,6 @@ fail:
 
 void pa__done(pa_module*m) {
     struct userdata *u;
-    struct device_info *d;
 
     pa_assert(m);
 
@@ -463,10 +518,7 @@ void pa__done(pa_module*m) {
     if (u->source_output_state_changed_slot)
         pa_hook_slot_free(u->source_output_state_changed_slot);
 
-    while ((d = pa_hashmap_steal_first(u->device_infos)))
-        device_info_free(d);
-
-    pa_hashmap_free(u->device_infos, NULL, NULL);
+    pa_hashmap_free(u->device_infos, (pa_free_cb_t) device_info_free);
 
     pa_xfree(u);
 }