]> code.delx.au - pulseaudio/blobdiff - src/modules/module-rescue-streams.c
bluetooth: Fix crash in pa_bluetooth_discovery_get_device_by_address()
[pulseaudio] / src / modules / module-rescue-streams.c
index e0eed0361a8bf90737cff223209b66c5d85c00c7..7035a35097da47c1a1f4cf50b6a678518a47b9e1 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 <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
 
 #include "module-rescue-streams-symdef.h"
 
 
 #include "module-rescue-streams-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move their streams to the default sink/source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("When a sink/source is removed, try to move its streams to the default sink/source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(true);
 
 static const char* const valid_modargs[] = {
     NULL,
 };
 
 struct userdata {
 
 static const char* const valid_modargs[] = {
     NULL,
 };
 
 struct userdata {
-    pa_hook_slot *sink_slot, *source_slot;
+    pa_hook_slot
+        *sink_unlink_slot,
+        *source_unlink_slot,
+        *sink_input_move_fail_slot,
+        *source_output_move_fail_slot;
 };
 
 };
 
-static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
+static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) {
+    pa_sink *target, *def;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(i);
+
+    def = pa_namereg_get_default_sink(c);
+
+    if (def && def != skip && pa_sink_input_may_move_to(i, def))
+        return def;
+
+    PA_IDXSET_FOREACH(target, c->sinks, idx) {
+        if (target == def)
+            continue;
+
+        if (target == skip)
+            continue;
+
+        if (!PA_SINK_IS_LINKED(pa_sink_get_state(target)))
+            continue;
+
+        if (pa_sink_input_may_move_to(i, target))
+            return target;
+    }
+
+    pa_log_debug("No evacuation sink found.");
+    return NULL;
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
     pa_sink_input *i;
     pa_sink_input *i;
-    pa_sink *target;
+    uint32_t idx;
 
     pa_assert(c);
     pa_assert(sink);
 
 
     pa_assert(c);
     pa_assert(sink);
 
-    if (!pa_idxset_size(sink->inputs)) {
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (pa_idxset_size(sink->inputs) <= 0) {
         pa_log_debug("No sink inputs to move away.");
         return PA_HOOK_OK;
     }
 
         pa_log_debug("No sink inputs to move away.");
         return PA_HOOK_OK;
     }
 
-    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
-        uint32_t idx;
-        
-        for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
-            if (target != sink)
-                break;
-
-        if (!target) {
-            pa_log_info("No evacuation sink found.");
-            return PA_HOOK_OK;
-        }
+    PA_IDXSET_FOREACH(i, sink->inputs, idx) {
+        pa_sink *target;
+
+        if (!(target = find_evacuation_sink(c, i, sink)))
+            continue;
+
+        if (pa_sink_input_move_to(i, target, false) < 0)
+            pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        else
+            pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
     }
 
     }
 
-    while ((i = pa_idxset_first(sink->inputs, NULL))) {
-        if (pa_sink_input_move_to(i, target, 1) < 0) {
-            pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, i->name, target->name);
-            return PA_HOOK_OK;
-        }
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_input *i, void *userdata) {
+    pa_sink *target;
+
+    pa_assert(c);
+    pa_assert(i);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (!(target = find_evacuation_sink(c, i, NULL)))
+        return PA_HOOK_OK;
+
+    if (pa_sink_input_finish_move(i, target, false) < 0) {
+        pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_OK;
 
 
-        pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, i->name, target->name);
+    } else {
+        pa_log_info("Successfully moved sink input %u \"%s\" to %s.", i->index,
+                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_STOP;
     }
     }
+}
 
 
+static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) {
+    pa_source *target, *def;
+    uint32_t idx;
 
 
-    return PA_HOOK_OK;
+    pa_assert(c);
+    pa_assert(o);
+
+    def = pa_namereg_get_default_source(c);
+
+    if (def && def != skip && pa_source_output_may_move_to(o, def))
+        return def;
+
+    PA_IDXSET_FOREACH(target, c->sources, idx) {
+        if (target == def)
+            continue;
+
+        if (target == skip)
+            continue;
+
+        if (skip && !target->monitor_of != !skip->monitor_of)
+            continue;
+
+        if (!PA_SOURCE_IS_LINKED(pa_source_get_state(target)))
+            continue;
+
+        if (pa_source_output_may_move_to(o, target))
+            return target;
+    }
+
+    pa_log_debug("No evacuation source found.");
+    return NULL;
 }
 
 }
 
-static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void* userdata) {
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
     pa_source_output *o;
     pa_source_output *o;
-    pa_source *target;
+    uint32_t idx;
 
     pa_assert(c);
     pa_assert(source);
 
 
     pa_assert(c);
     pa_assert(source);
 
-    if (!pa_idxset_size(source->outputs)) {
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (pa_idxset_size(source->outputs) <= 0) {
         pa_log_debug("No source outputs to move away.");
         return PA_HOOK_OK;
     }
 
         pa_log_debug("No source outputs to move away.");
         return PA_HOOK_OK;
     }
 
-    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
-        uint32_t idx;
+    PA_IDXSET_FOREACH(o, source->outputs, idx) {
+        pa_source *target;
 
 
-        for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
-            if (target != source && !target->monitor_of == !source->monitor_of)
-                break;
+        if (!(target = find_evacuation_source(c, o, source)))
+            continue;
 
 
-        if (!target) {
-            pa_log_info("No evacuation source found.");
-            return PA_HOOK_OK;
-        }
+        if (pa_source_output_move_to(o, target, false) < 0)
+            pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
+                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        else
+            pa_log_info("Successfully moved source output %u \"%s\" to %s.", o->index,
+                        pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
     }
 
     }
 
-    pa_assert(target != source);
+    return PA_HOOK_OK;
+}
 
 
-    while ((o = pa_idxset_first(source->outputs, NULL))) {
-        if (pa_source_output_move_to(o, target) < 0) {
-            pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, o->name, target->name);
-            return PA_HOOK_OK;
-        }
+static pa_hook_result_t source_output_move_fail_hook_callback(pa_core *c, pa_source_output *i, void *userdata) {
+    pa_source *target;
 
 
-        pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, o->name, target->name);
-    }
+    pa_assert(c);
+    pa_assert(i);
 
 
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
 
 
-    return PA_HOOK_OK;
+    if (!(target = find_evacuation_source(c, i, NULL)))
+        return PA_HOOK_OK;
+
+    if (pa_source_output_finish_move(i, target, false) < 0) {
+        pa_log_info("Failed to move source input %u \"%s\" to %s.", i->index,
+                        pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_OK;
+
+    } else {
+        pa_log_info("Successfully moved source input %u \"%s\" to %s.", i->index,
+                    pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
+        return PA_HOOK_STOP;
+    }
 }
 
 int pa__init(pa_module*m) {
 }
 
 int pa__init(pa_module*m) {
-    pa_modargs *ma = NULL;
+    pa_modargs *ma;
     struct userdata *u;
 
     pa_assert(m);
     struct userdata *u;
 
     pa_assert(m);
@@ -138,8 +243,13 @@ int pa__init(pa_module*m) {
     }
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     }
 
     m->userdata = u = pa_xnew(struct userdata, 1);
-    u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_DISCONNECT], (pa_hook_cb_t) sink_hook_callback, NULL);
-    u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_DISCONNECT], (pa_hook_cb_t) source_hook_callback, NULL);
+
+    /* A little bit later than module-stream-restore, module-intended-roles... */
+    u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+    u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_unlink_hook_callback, u);
+
+    u->sink_input_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_input_move_fail_hook_callback, u);
+    u->source_output_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) source_output_move_fail_hook_callback, u);
 
     pa_modargs_free(ma);
     return 0;
 
     pa_modargs_free(ma);
     return 0;
@@ -150,14 +260,18 @@ void pa__done(pa_module*m) {
 
     pa_assert(m);
 
 
     pa_assert(m);
 
-    if (!m->userdata)
+    if (!(u = m->userdata))
         return;
 
         return;
 
-    u = m->userdata;
-    if (u->sink_slot)
-        pa_hook_slot_free(u->sink_slot);
-    if (u->source_slot)
-        pa_hook_slot_free(u->source_slot);
+    if (u->sink_unlink_slot)
+        pa_hook_slot_free(u->sink_unlink_slot);
+    if (u->source_unlink_slot)
+        pa_hook_slot_free(u->source_unlink_slot);
+
+    if (u->sink_input_move_fail_slot)
+        pa_hook_slot_free(u->sink_input_move_fail_slot);
+    if (u->source_output_move_fail_slot)
+        pa_hook_slot_free(u->source_output_move_fail_slot);
 
     pa_xfree(u);
 }
 
     pa_xfree(u);
 }