]> code.delx.au - pulseaudio/blobdiff - src/modules/module-suspend-on-idle.c
Use pa_hashmap_remove_and_free() where appropriate
[pulseaudio] / src / modules / module-suspend-on-idle.c
index a398597477cf5261c407e1d43a58b6f25812e54e..6fbe6406ae52133c88259d96c15c148f2a110192 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
 /***
   This file is part of PulseAudio.
 
@@ -7,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
 
   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
   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/xmalloc.h>
 #include <pulse/timeval.h>
+#include <pulse/rtclock.h>
 
 #include <pulsecore/core.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/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);
 
 #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",
 
 static const char* const valid_modargs[] = {
     "timeout",
@@ -64,8 +64,10 @@ struct userdata {
         *source_output_new_slot,
         *sink_input_unlink_slot,
         *source_output_unlink_slot,
         *source_output_new_slot,
         *sink_input_unlink_slot,
         *source_output_unlink_slot,
-        *sink_input_move_slot,
-        *source_output_move_slot,
+        *sink_input_move_start_slot,
+        *source_output_move_start_slot,
+        *sink_input_move_finish_slot,
+        *source_output_move_finish_slot,
         *sink_input_state_changed_slot,
         *source_output_state_changed_slot;
 };
         *sink_input_state_changed_slot,
         *source_output_state_changed_slot;
 };
@@ -74,41 +76,44 @@ struct device_info {
     struct userdata *userdata;
     pa_sink *sink;
     pa_source *source;
     struct userdata *userdata;
     pa_sink *sink;
     pa_source *source;
-    struct timeval last_use;
+    pa_usec_t last_use;
     pa_time_event *time_event;
     pa_time_event *time_event;
+    pa_usec_t timeout;
 };
 
 };
 
-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);
 
     struct device_info *d = userdata;
 
     pa_assert(d);
 
     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
 
-    if (d->sink && pa_sink_used_by(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_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_used_by(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_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) {
     }
 }
 
 static void restart(struct device_info *d) {
-    struct timeval tv;
+    pa_usec_t now;
+
     pa_assert(d);
     pa_assert(d);
+    pa_assert(d->sink || d->source);
 
 
-    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);
+    d->last_use = now = pa_rtclock_now();
+    pa_core_rttime_restart(d->userdata->core, d->time_event, now + d->timeout);
 
     if (d->sink)
 
     if (d->sink)
-        pa_log_debug("Sink %s becomes idle.", d->sink->name);
+        pa_log_debug("Sink %s becomes idle, timeout in %" PRIu64 " seconds.", d->sink->name, d->timeout / PA_USEC_PER_SEC);
     if (d->source)
     if (d->source)
-        pa_log_debug("Source %s becomes idle.", d->source->name);
+        pa_log_debug("Source %s becomes idle, timeout in %" PRIu64 " seconds.", d->source->name, d->timeout / PA_USEC_PER_SEC);
 }
 
 static void resume(struct device_info *d) {
 }
 
 static void resume(struct device_info *d) {
@@ -117,15 +122,13 @@ static void resume(struct device_info *d) {
     d->userdata->core->mainloop->time_restart(d->time_event, NULL);
 
     if (d->sink) {
     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) {
     }
 
     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,8 +139,16 @@ static pa_hook_result_t sink_input_fixate_hook_cb(pa_core *c, pa_sink_input_new_
     pa_assert(data);
     pa_assert(u);
 
     pa_assert(data);
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, data->sink)))
+    /* 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. In that case,
+     * make sure we close the sink again after the timeout interval. */
+
+    if ((d = pa_hashmap_get(u->device_infos, data->sink))) {
         resume(d);
         resume(d);
+        if (pa_sink_check_suspend(d->sink) <= 0)
+            restart(d);
+    }
 
     return PA_HOOK_OK;
 }
 
     return PA_HOOK_OK;
 }
@@ -149,8 +160,23 @@ static pa_hook_result_t source_output_fixate_hook_cb(pa_core *c, pa_source_outpu
     pa_assert(data);
     pa_assert(u);
 
     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);
         resume(d);
+        if (d->source) {
+            if (pa_source_check_suspend(d->source) <= 0)
+                restart(d);
+        } else {
+            /* The source output is connected to a monitor source. */
+            pa_assert(d->sink);
+            if (pa_sink_check_suspend(d->sink) <= 0)
+                restart(d);
+        }
+    }
 
     return PA_HOOK_OK;
 }
 
     return PA_HOOK_OK;
 }
@@ -160,7 +186,10 @@ 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);
 
     pa_sink_input_assert_ref(s);
     pa_assert(u);
 
-    if (pa_sink_used_by(s->sink) <= 0) {
+    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)))
             restart(d);
         struct device_info *d;
         if ((d = pa_hashmap_get(u->device_infos, s->sink)))
             restart(d);
@@ -170,49 +199,99 @@ 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) {
 }
 
 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);
 
     pa_assert(c);
     pa_source_output_assert_ref(s);
     pa_assert(u);
 
-    if (pa_source_used_by(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;
 }
 
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
     struct device_info *d;
 
     pa_assert(c);
     struct device_info *d;
 
     pa_assert(c);
-    pa_assert(data);
+    pa_sink_input_assert_ref(s);
     pa_assert(u);
 
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
-        resume(d);
-
-    if (pa_sink_used_by(data->sink_input->sink) <= 1)
-        if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
+    if (pa_sink_check_suspend(s->sink) <= 1)
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
             restart(d);
 
     return PA_HOOK_OK;
 }
 
             restart(d);
 
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
+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;
     struct device_info *d;
+    pa_sink_input_state_t state;
 
     pa_assert(c);
 
     pa_assert(c);
-    pa_assert(data);
+    pa_sink_input_assert_ref(s);
     pa_assert(u);
 
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+    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);
 
         resume(d);
 
-    if (pa_source_used_by(data->source_output->source) <= 1)
-        if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
-            restart(d);
+    return PA_HOOK_OK;
+}
+
+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 = NULL;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    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;
+}
+
+static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    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;
 }
 
     return PA_HOOK_OK;
 }
@@ -220,6 +299,7 @@ static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_
 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;
 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);
     pa_assert(c);
     pa_sink_input_assert_ref(s);
     pa_assert(u);
@@ -233,16 +313,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) {
 }
 
 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);
 
     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);
             resume(d);
+    }
 
     return PA_HOOK_OK;
 }
 
     return PA_HOOK_OK;
 }
@@ -251,6 +336,9 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user
     struct device_info *d;
     pa_source *source;
     pa_sink *sink;
     struct device_info *d;
     pa_source *source;
     pa_sink *sink;
+    const char *timeout_str;
+    int32_t timeout;
+    bool timeout_valid;
 
     pa_assert(c);
     pa_object_assert_ref(o);
 
     pa_assert(c);
     pa_object_assert_ref(o);
@@ -259,17 +347,36 @@ 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;
 
     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);
 
     pa_assert(source || sink);
 
+    timeout_str = pa_proplist_gets(sink ? sink->proplist : source->proplist, "module-suspend-on-idle.timeout");
+    if (timeout_str && pa_atoi(timeout_str, &timeout) >= 0)
+        timeout_valid = true;
+    else
+        timeout_valid = false;
+
+    if (timeout_valid && timeout < 0)
+        return PA_HOOK_OK;
+
     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 = 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);
+
+    if (timeout_valid)
+        d->timeout = timeout * PA_USEC_PER_SEC;
+    else
+        d->timeout = d->userdata->timeout;
+
     pa_hashmap_put(u->device_infos, o, d);
 
     pa_hashmap_put(u->device_infos, o, d);
 
-    if ((d->sink && pa_sink_used_by(d->sink) <= 0) ||
-        (d->source && pa_source_used_by(d->source) <= 0))
+    if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) ||
+        (d->source && pa_source_check_suspend(d->source) <= 0))
         restart(d);
 
     return PA_HOOK_OK;
         restart(d);
 
     return PA_HOOK_OK;
@@ -289,14 +396,11 @@ static void device_info_free(struct device_info *d) {
 }
 
 static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
 }
 
 static pa_hook_result_t device_unlink_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
-    struct device_info *d;
-
     pa_assert(c);
     pa_object_assert_ref(o);
     pa_assert(u);
 
     pa_assert(c);
     pa_object_assert_ref(o);
     pa_assert(u);
 
-    if ((d = pa_hashmap_remove(u->device_infos, o)))
-        device_info_free(d);
+    pa_hashmap_remove_and_free(u->device_infos, o);
 
     return PA_HOOK_OK;
 }
 
     return PA_HOOK_OK;
 }
@@ -315,22 +419,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);
 
         pa_sink *s = PA_SINK(o);
         pa_sink_state_t state = pa_sink_get_state(s);
 
-        if (pa_sink_used_by(s) <= 0) {
-
+        if (pa_sink_check_suspend(s) <= 0)
             if (PA_SINK_IS_OPENED(state))
                 restart(d);
 
             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);
 
     } 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_used_by(s) <= 0) {
-
+        if (pa_source_check_suspend(s) <= 0)
             if (PA_SOURCE_IS_OPENED(state))
                 restart(d);
             if (PA_SOURCE_IS_OPENED(state))
                 restart(d);
-        }
     }
 
     return PA_HOOK_OK;
     }
 
     return PA_HOOK_OK;
@@ -339,7 +438,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
-    uint32_t timeout = 1;
+    uint32_t timeout = 5;
     uint32_t idx;
     pa_sink *sink;
     pa_source *source;
     uint32_t idx;
     pa_sink *sink;
     pa_source *source;
@@ -358,30 +457,32 @@ int pa__init(pa_module*m) {
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
-    u->timeout = timeout;
-    u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    u->timeout = timeout * PA_USEC_PER_SEC;
+    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);
 
 
-    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);
 
         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);
 
         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_cb_t) device_new_hook_cb, u);
-    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], (pa_hook_cb_t) device_new_hook_cb, u);
-    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u);
-    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u);
-    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u);
-    u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u);
-
-    u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
-    u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], (pa_hook_cb_t) source_output_fixate_hook_cb, u);
-    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
-    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], (pa_hook_cb_t) source_output_unlink_hook_cb, u);
-    u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], (pa_hook_cb_t) sink_input_move_hook_cb, u);
-    u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], (pa_hook_cb_t) source_output_move_hook_cb, u);
-    u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
-    u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], (pa_hook_cb_t) source_output_state_changed_hook_cb, 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);
+    u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_new_hook_cb, u);
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_hook_cb, u);
+    u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+    u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_state_changed_hook_cb, u);
+
+    u->sink_input_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_fixate_hook_cb, u);
+    u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
+    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
+    u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
+    u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u);
+    u->source_output_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u);
+    u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u);
+    u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u);
+    u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
+    u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
 
     pa_modargs_free(ma);
     return 0;
 
     pa_modargs_free(ma);
     return 0;
@@ -396,7 +497,6 @@ fail:
 
 void pa__done(pa_module*m) {
     struct userdata *u;
 
 void pa__done(pa_module*m) {
     struct userdata *u;
-    struct device_info *d;
 
     pa_assert(m);
 
 
     pa_assert(m);
 
@@ -423,8 +523,10 @@ void pa__done(pa_module*m) {
         pa_hook_slot_free(u->sink_input_new_slot);
     if (u->sink_input_unlink_slot)
         pa_hook_slot_free(u->sink_input_unlink_slot);
         pa_hook_slot_free(u->sink_input_new_slot);
     if (u->sink_input_unlink_slot)
         pa_hook_slot_free(u->sink_input_unlink_slot);
-    if (u->sink_input_move_slot)
-        pa_hook_slot_free(u->sink_input_move_slot);
+    if (u->sink_input_move_start_slot)
+        pa_hook_slot_free(u->sink_input_move_start_slot);
+    if (u->sink_input_move_finish_slot)
+        pa_hook_slot_free(u->sink_input_move_finish_slot);
     if (u->sink_input_state_changed_slot)
         pa_hook_slot_free(u->sink_input_state_changed_slot);
 
     if (u->sink_input_state_changed_slot)
         pa_hook_slot_free(u->sink_input_state_changed_slot);
 
@@ -432,15 +534,14 @@ void pa__done(pa_module*m) {
         pa_hook_slot_free(u->source_output_new_slot);
     if (u->source_output_unlink_slot)
         pa_hook_slot_free(u->source_output_unlink_slot);
         pa_hook_slot_free(u->source_output_new_slot);
     if (u->source_output_unlink_slot)
         pa_hook_slot_free(u->source_output_unlink_slot);
-    if (u->source_output_move_slot)
-        pa_hook_slot_free(u->source_output_move_slot);
+    if (u->source_output_move_start_slot)
+        pa_hook_slot_free(u->source_output_move_start_slot);
+    if (u->source_output_move_finish_slot)
+        pa_hook_slot_free(u->source_output_move_finish_slot);
     if (u->source_output_state_changed_slot)
         pa_hook_slot_free(u->source_output_state_changed_slot);
 
     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_xfree(u);
 }
 
     pa_xfree(u);
 }