diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/module-device-manager.c | 206 | 
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);  | 
