X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=pulse.c;h=f16d0abf6b769cb5d4493e53efed89052c8d463f;hb=81d4eeca11f67db9510c70a8c76ff9a9c30b3799;hp=479a5e1055c9daf3ec08219f91d6cf6814b88874;hpb=8c6ae9672d2e5326b9216c27718e40fb1c3a18b2;p=ursary.git diff --git a/pulse.c b/pulse.c index 479a5e1..f16d0ab 100644 --- a/pulse.c +++ b/pulse.c @@ -1,10 +1,10 @@ /* * Asynchronous Interface to PulseAudio * - * (c) 2014 Martin Mares + * (c) 2014--2020 Martin Mares */ -#define LOCAL_DEBUG +#undef LOCAL_DEBUG #include #include @@ -17,10 +17,21 @@ #include "ursaryd.h" +enum pulse_state { + PS_OFFLINE, + PS_SUBSCRIBE, + PS_GET_CLIENTS, + PS_GET_SINKS, + PS_GET_SINK_INPUTS, + PS_GET_SOURCES, + PS_GET_SERVER, + PS_ONLINE, +}; + enum pulse_state pulse_state; #define PULSE_STATE(s) do { pulse_state = s; DBG("Pulse: " #s); } while (0) -clist pulse_client_list, pulse_sink_list, pulse_sink_input_list; +clist pulse_client_list, pulse_source_list, pulse_sink_list, pulse_sink_input_list; static pa_context *pulse_ctx; static struct main_timer pulse_connect_timer; @@ -81,8 +92,8 @@ void pulse_dump(void) msg(L_DEBUG, "## Client #%d: %s host=%s", c->idx, c->name, c->host); CLIST_FOR_EACH(struct pulse_sink *, s, pulse_sink_list) - msg(L_DEBUG, "## Sink #%d: %s channels=%u volume=%u base_vol=%u mute=%u", - s->idx, s->name, s->channels, s->volume, s->base_volume, s->mute); + msg(L_DEBUG, "## Sink #%d: %s channels=%u volume=%u base_vol=%u mute=%u suspended=%u port=%s", + s->idx, s->name, s->channels, s->volume, s->base_volume, s->mute, s->suspended, s->active_port); CLIST_FOR_EACH(struct pulse_sink_input *, s, pulse_sink_input_list) msg(L_DEBUG, "## Sink input #%d: %s client=%d sink=%d channels=%u volume=%u mute=%u", @@ -128,6 +139,87 @@ void pulse_server_set_default_sink(const char *name) PULSE_ASYNC_RUN(pa_context_set_default_sink, name, pulse_success_cb); } +/*** Sources ***/ + +#define HASH_NODE struct pulse_source +#define HASH_PREFIX(x) pulse_source_##x +#define HASH_KEY_ATOMIC idx +#define HASH_WANT_CLEANUP +#define HASH_WANT_LOOKUP +#define HASH_WANT_REMOVE +#define HASH_ZERO_FILL +#include + +static void pulse_source_cb(pa_context *ctx UNUSED, const pa_source_info *i, int eol, void *userdata) +{ + struct pulse_op *op = userdata; + + if (eol) + { + if (op->is_init) + { + PULSE_STATE(PS_GET_SERVER); + PULSE_ASYNC_INIT_RUN(pa_context_get_server_info, pulse_server_cb); + } + pulse_op_done(op); + return; + } + + DBG("Pulse: SOURCE #%u: %s (%s) flags=%08x channels=%u volume=%u mute=%d base_vol=%u state=%u port=%s", + i->index, i->name, i->description, i->flags, i->channel_map.channels, i->volume.values[0], i->mute, i->base_volume, i->state, + (i->active_port ? i->active_port->name : "none")); + pulse_dump_proplist(i->proplist); + + struct pulse_source *s = pulse_source_lookup(i->index); + if (!clist_is_linked(&s->n)) + clist_add_tail(&pulse_source_list, &s->n); + SET_STRING(s->name, i->name); + s->channels = i->channel_map.channels; + s->volume = pa_cvolume_avg(&i->volume); + s->base_volume = i->base_volume; + s->mute = i->mute; + s->suspended = (i->state == PA_SOURCE_SUSPENDED); + SET_STRING(s->active_port, (i->active_port ? i->active_port->name : "none")); + schedule_update(); +} + +static void pulse_source_gone(int idx) +{ + DBG("Pulse: REMOVE SOURCE #%d", idx); + struct pulse_source *s = pulse_source_lookup(idx); + clist_remove(&s->n); + pulse_source_remove(s); + schedule_update(); +} + +struct pulse_source *pulse_source_by_name(const char *name) +{ + CLIST_FOR_EACH(struct pulse_source *, s, pulse_source_list) + if (!strcmp(s->name, name)) + return s; + return NULL; +} + +struct pulse_source *pulse_source_by_idx(int idx) +{ + return pulse_source_lookup(idx); +} + +void pulse_source_set_volume(int idx, pa_cvolume *cvol) +{ + PULSE_ASYNC_RUN(pa_context_set_source_volume_by_index, idx, cvol, pulse_success_cb); +} + +void pulse_source_set_mute(int idx, bool mute) +{ + PULSE_ASYNC_RUN(pa_context_set_source_mute_by_index, idx, mute, pulse_success_cb); +} + +void pulse_source_set_port(int idx, const char *port) +{ + PULSE_ASYNC_RUN(pa_context_set_source_port_by_index, idx, port, pulse_success_cb); +} + /*** Sink inputs ***/ #define HASH_NODE struct pulse_sink_input @@ -148,8 +240,8 @@ static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info { if (op->is_init) { - PULSE_STATE(PS_GET_SERVER); - PULSE_ASYNC_INIT_RUN(pa_context_get_server_info, pulse_server_cb); + PULSE_STATE(PS_GET_SOURCES); + PULSE_ASYNC_INIT_RUN(pa_context_get_source_info_list, pulse_source_cb); } pulse_op_done(op); return; @@ -226,8 +318,9 @@ static void pulse_sink_cb(pa_context *ctx UNUSED, const pa_sink_info *i, int eol return; } - DBG("Pulse: SINK #%u: %s (%s) flags=%08x channels=%u volume=%u mute=%d base_vol=%u state=%u", - i->index, i->name, i->description, i->flags, i->channel_map.channels, i->volume.values[0], i->mute, i->base_volume, i->state); + DBG("Pulse: SINK #%u: %s (%s) flags=%08x channels=%u volume=%u mute=%d base_vol=%u state=%u port=%s", + i->index, i->name, i->description, i->flags, i->channel_map.channels, i->volume.values[0], i->mute, i->base_volume, i->state, + (i->active_port ? i->active_port->name : "none")); pulse_dump_proplist(i->proplist); struct pulse_sink *s = pulse_sink_lookup(i->index); @@ -238,6 +331,8 @@ static void pulse_sink_cb(pa_context *ctx UNUSED, const pa_sink_info *i, int eol s->volume = pa_cvolume_avg(&i->volume); s->base_volume = i->base_volume; s->mute = i->mute; + s->suspended = (i->state == PA_SINK_SUSPENDED); + SET_STRING(s->active_port, (i->active_port ? i->active_port->name : "none")); schedule_update(); } @@ -273,12 +368,18 @@ void pulse_sink_set_mute(int idx, bool mute) PULSE_ASYNC_RUN(pa_context_set_sink_mute_by_index, idx, mute, pulse_success_cb); } +void pulse_sink_set_port(int idx, const char *port) +{ + PULSE_ASYNC_RUN(pa_context_set_sink_port_by_index, idx, port, pulse_success_cb); +} + /*** Clients ***/ #define HASH_NODE struct pulse_client #define HASH_PREFIX(x) pulse_client_##x #define HASH_KEY_ATOMIC idx #define HASH_WANT_CLEANUP +#define HASH_WANT_FIND #define HASH_WANT_LOOKUP #define HASH_WANT_REMOVE #define HASH_ZERO_FILL @@ -315,15 +416,18 @@ static void pulse_client_cb(pa_context *ctx UNUSED, const pa_client_info *i, int static void pulse_client_gone(int idx) { DBG("Pulse: REMOVE CLIENT #%d", idx); - struct pulse_client *c = pulse_client_lookup(idx); - clist_remove(&c->n); - pulse_client_remove(c); - schedule_update(); + struct pulse_client *c = pulse_client_find(idx); + if (c) + { + clist_remove(&c->n); + pulse_client_remove(c); + schedule_update(); + } } struct pulse_client *pulse_client_by_idx(int idx) { - return pulse_client_lookup(idx); + return pulse_client_find(idx); } /*** Events ***/ @@ -353,6 +457,12 @@ static void pulse_event_cb(pa_context *ctx UNUSED, pa_subscription_event_type_t else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) pulse_client_gone(idx); break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) + PULSE_ASYNC_RUN(pa_context_get_source_info_by_index, idx, pulse_source_cb); + else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) + pulse_source_gone(idx); + break; case PA_SUBSCRIPTION_EVENT_SINK: if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) PULSE_ASYNC_RUN(pa_context_get_sink_info_by_index, idx, pulse_sink_cb); @@ -378,6 +488,7 @@ static void pulse_shutdown(void) { DBG("Pulse: Shutting down"); pulse_client_cleanup(); + pulse_source_cleanup(); pulse_sink_cleanup(); pulse_sink_input_cleanup(); } @@ -417,9 +528,11 @@ static void pulse_connect(struct main_timer *t) clist_init(&pulse_op_list); clist_init(&pulse_client_list); + clist_init(&pulse_source_list); clist_init(&pulse_sink_list); clist_init(&pulse_sink_input_list); pulse_client_init(); + pulse_source_init(); pulse_sink_init(); pulse_sink_input_init(); @@ -431,6 +544,11 @@ static void pulse_connect(struct main_timer *t) pa_context_connect(pulse_ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); } +bool pulse_is_ready(void) +{ + return (pulse_state == PS_ONLINE); +} + void pulse_init(void) { pmain_init();