summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/modules/module-device-manager.c206
1 files changed, 201 insertions, 5 deletions
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index e029c1d8..4c4e3f0b 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -58,11 +58,15 @@ PA_MODULE_AUTHOR("Colin Guthrie");
PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("This module does not take any arguments");
+PA_MODULE_USAGE(
+ "on_hotplug=<When new device becomes available, recheck streams?> "
+ "on_rescue=<When device becomes unavailable, recheck streams?>");
#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
static const char* const valid_modargs[] = {
+ "on_hotplug",
+ "on_rescue",
NULL
};
@@ -73,6 +77,12 @@ struct userdata {
pa_hook_slot
*sink_new_hook_slot,
*source_new_hook_slot,
+ *sink_input_new_hook_slot,
+ *source_output_new_hook_slot,
+ *sink_put_hook_slot,
+ *source_put_hook_slot,
+ *sink_unlink_hook_slot,
+ *source_unlink_hook_slot,
*connection_unlink_hook_slot;
pa_time_event *save_time_event;
pa_database *database;
@@ -80,6 +90,8 @@ struct userdata {
pa_native_protocol *protocol;
pa_idxset *subscribed;
+ pa_bool_t on_hotplug;
+ pa_bool_t on_rescue;
pa_bool_t role_device_priority_routing;
pa_bool_t stream_restore_used;
pa_bool_t checked_stream_restore;
@@ -100,10 +112,12 @@ enum {
ROLE_A11Y,
};
+typedef uint32_t role_indexes_t[NUM_ROLES];
+
struct entry {
uint8_t version;
char description[PA_NAME_MAX];
- uint32_t priority[NUM_ROLES];
+ role_indexes_t priority;
} PA_GCC_PACKED;
enum {
@@ -215,7 +229,7 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
*entry = *old;
else {
/* This is a new device, so make sure we write it's priority list correctly */
- uint32_t max_priority[NUM_ROLES];
+ role_indexes_t max_priority;
pa_datum key;
pa_bool_t done;
@@ -390,6 +404,138 @@ static char *get_name(const char *key, const char *prefix) {
return t;
}
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+ char *name;
+ struct entry *e;
+
+ pa_assert(c);
+ pa_assert(new_data);
+ pa_assert(u);
+
+ if (!u->role_device_priority_routing)
+ return PA_HOOK_OK;
+
+ /*if (!(name = get_name(new_data->proplist, "sink-input")))
+ return PA_HOOK_OK;
+
+ if (new_data->sink)
+ pa_log_debug("Not restoring device for stream %s, because already set.", name);
+ else if ((e = read_entry(u, name))) {
+
+ pa_xfree(e);
+ }
+
+ pa_xfree(name);*/
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+ char *name;
+ struct entry *e;
+
+ pa_assert(c);
+ pa_assert(new_data);
+ pa_assert(u);
+
+ if (!u->role_device_priority_routing)
+ return PA_HOOK_OK;
+
+ if (new_data->direct_on_input)
+ return PA_HOOK_OK;
+
+ /*if (!(name = get_name(new_data->proplist, "source-output")))
+ return PA_HOOK_OK;
+
+ if (new_data->source)
+ pa_log_debug("Not restoring device for stream %s, because already set", name);
+ else if ((e = read_entry(u, name))) {
+
+ pa_xfree(e);
+ }
+
+ pa_xfree(name);*/
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+ pa_sink_input *si;
+ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(sink);
+ pa_assert(u);
+ pa_assert(u->on_hotplug);
+
+ if (!u->role_device_priority_routing)
+ return PA_HOOK_OK;
+
+ /** @todo Ensure redo the routing based on the priorities */
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+ pa_source_output *so;
+ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(source);
+ pa_assert(u);
+ pa_assert(u->on_hotplug);
+
+ if (!u->role_device_priority_routing)
+ return PA_HOOK_OK;
+
+ /** @todo Ensure redo the routing based on the priorities */
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+ pa_sink_input *si;
+ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(sink);
+ pa_assert(u);
+ pa_assert(u->on_rescue);
+
+ /* 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 (!u->role_device_priority_routing)
+ return PA_HOOK_OK;
+
+ /** @todo Ensure redo the routing based on the priorities */
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+ pa_source_output *so;
+ uint32_t idx;
+
+ pa_assert(c);
+ pa_assert(source);
+ pa_assert(u);
+ pa_assert(u->on_rescue);
+
+ /* 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 (!u->role_device_priority_routing)
+ return PA_HOOK_OK;
+
+ /** @todo Ensure redo the routing based on the priorities */
+
+ return PA_HOOK_OK;
+}
+
+
static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
pa_sink *sink;
pa_source *source;
@@ -781,7 +927,10 @@ int pa__init(pa_module*m) {
char *fname;
pa_sink *sink;
pa_source *source;
+ pa_sink_input *si;
+ pa_source_output *so;
uint32_t idx;
+ pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
pa_assert(m);
@@ -790,9 +939,17 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
+ pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
+ pa_log("on_hotplug= and on_rescue= expect boolean arguments");
+ goto fail;
+ }
+
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
+ u->on_hotplug = on_hotplug;
+ u->on_rescue = on_rescue;
u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
u->protocol = pa_native_protocol_get(m->core);
@@ -802,9 +959,27 @@ int pa__init(pa_module*m) {
u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+ /* Used to handle device description management */
u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
+ /* The following slots are used to deal with routing */
+ /* A little bit later than module-stream-restore, module-intended-roles */
+ u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+ u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
+
+ if (on_hotplug) {
+ /* A little bit later than module-stream-restore, module-intended-roles */
+ u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
+ u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
+ }
+
+ if (on_rescue) {
+ /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
+ u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+ u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
+ }
+
if (!(fname = pa_state_path("device-manager", TRUE)))
goto fail;
@@ -817,12 +992,18 @@ int pa__init(pa_module*m) {
pa_log_info("Sucessfully opened database file '%s'.", fname);
pa_xfree(fname);
- 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)
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, 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)
subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+ PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
+
+ PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
+
pa_modargs_free(ma);
return 0;
@@ -851,6 +1032,21 @@ void pa__done(pa_module*m) {
if (u->source_new_hook_slot)
pa_hook_slot_free(u->source_new_hook_slot);
+ if (u->sink_input_new_hook_slot)
+ pa_hook_slot_free(u->sink_input_new_hook_slot);
+ if (u->source_output_new_hook_slot)
+ pa_hook_slot_free(u->source_output_new_hook_slot);
+
+ if (u->sink_put_hook_slot)
+ pa_hook_slot_free(u->sink_put_hook_slot);
+ if (u->source_put_hook_slot)
+ pa_hook_slot_free(u->source_put_hook_slot);
+
+ if (u->sink_unlink_hook_slot)
+ pa_hook_slot_free(u->sink_unlink_hook_slot);
+ if (u->source_unlink_hook_slot)
+ pa_hook_slot_free(u->source_unlink_hook_slot);
+
if (u->save_time_event)
u->core->mainloop->time_free(u->save_time_event);