X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=pulse.c;h=53478dcda6082d2097246c1998d7dd404c717f45;hb=9462e20b38064dc32852b1f60ad590d65e943f60;hp=08f51be9b36c9138462cb6b46763afa22cd31968;hpb=4d0bcfb647d3a22df51a1c15df105db69a3bf2d5;p=ursary.git diff --git a/pulse.c b/pulse.c index 08f51be..53478dc 100644 --- a/pulse.c +++ b/pulse.c @@ -1,7 +1,7 @@ /* * Asynchronous Interface to PulseAudio * - * (c) 2014 Martin Mares + * (c) 2014--2018 Martin Mares */ #undef LOCAL_DEBUG @@ -17,6 +17,16 @@ #include "ursaryd.h" +enum pulse_state { + PS_OFFLINE, + PS_SUBSCRIBE, + PS_GET_CLIENTS, + PS_GET_SINKS, + PS_GET_SINK_INPUTS, + PS_GET_SERVER, + PS_ONLINE, +}; + enum pulse_state pulse_state; #define PULSE_STATE(s) do { pulse_state = s; DBG("Pulse: " #s); } while (0) @@ -25,8 +35,6 @@ clist pulse_client_list, pulse_sink_list, pulse_sink_input_list; static pa_context *pulse_ctx; static struct main_timer pulse_connect_timer; -#define SET_STRING(_field, _val) do { if (!_field || strcmp(_field, _val)) { xfree(_field); _field = xstrdup(_val); } } while (0) - /*** Tracking of currently running asynchronous operations ***/ struct pulse_op { @@ -63,8 +71,8 @@ static void pulse_op_cancel_all(void) } } -#define PULSE_ASYNC_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->o = name(__VA_ARGS__, _op); } while (0) -#define PULSE_ASYNC_INIT_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->is_init = 1; _op->o = name(__VA_ARGS__, _op); } while (0) +#define PULSE_ASYNC_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->o = name(pulse_ctx, __VA_ARGS__, _op); } while (0) +#define PULSE_ASYNC_INIT_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->is_init = 1; _op->o = name(pulse_ctx, __VA_ARGS__, _op); } while (0) static void pulse_success_cb(pa_context *ctx UNUSED, int success, void *userdata) { @@ -77,16 +85,18 @@ static void pulse_success_cb(pa_context *ctx UNUSED, int success, void *userdata void pulse_dump(void) { + msg(L_DEBUG, "## Server: default_sink=%s", pulse_default_sink_name); + CLIST_FOR_EACH(struct pulse_client *, c, pulse_client_list) - DBG("## Client #%d: %s host=%s", c->idx, c->name, c->host); + msg(L_DEBUG, "## Client #%d: %s host=%s", c->idx, c->name, c->host); CLIST_FOR_EACH(struct pulse_sink *, s, pulse_sink_list) - DBG("## 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) - DBG("## Sink input #%d: %s client=%d sink=%d channels=%u volume=%u mute=%u", - s->idx, s->name, s->client_idx, s->sink_idx, s->channels, s->volume, s->mute); + msg(L_DEBUG, "## Sink input #%d: %s client=%d sink=%d channels=%u volume=%u mute=%u", + s->idx, s->name, s->client_idx, s->sink_idx, s->channels, s->volume, s->mute); } static void pulse_dump_proplist(pa_proplist *pl UNUSED) @@ -103,12 +113,38 @@ static void pulse_dump_proplist(pa_proplist *pl UNUSED) #endif } +/*** Server state ***/ + +char *pulse_default_sink_name; + +static void pulse_server_cb(pa_context *ctx UNUSED, const pa_server_info *i, void *userdata) +{ + struct pulse_op *op = userdata; + + DBG("Pulse: SERVER default_sink=%s", i->default_sink_name); + SET_STRING(pulse_default_sink_name, i->default_sink_name); + + if (op->is_init) + { + PULSE_STATE(PS_ONLINE); + msg(L_INFO, "PulseAudio is ready"); + } + pulse_op_done(op); + schedule_update(); +} + +void pulse_server_set_default_sink(const char *name) +{ + PULSE_ASYNC_RUN(pa_context_set_default_sink, name, pulse_success_cb); +} + /*** Sink inputs ***/ #define HASH_NODE struct pulse_sink_input #define HASH_PREFIX(x) pulse_sink_input_##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 @@ -122,9 +158,8 @@ static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info { if (op->is_init) { - PULSE_STATE(PS_ONLINE); - msg(L_INFO, "PulseAudio is ready"); - schedule_update(); + PULSE_STATE(PS_GET_SERVER); + PULSE_ASYNC_INIT_RUN(pa_context_get_server_info, pulse_server_cb); } pulse_op_done(op); return; @@ -149,20 +184,30 @@ static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info static void pulse_sink_input_gone(int idx) { DBG("Pulse: REMOVE SINK INPUT #%d", idx); - struct pulse_sink_input *s = pulse_sink_input_lookup(idx); - clist_remove(&s->n); - pulse_sink_input_remove(s); + struct pulse_sink_input *s = pulse_sink_input_find(idx); + if (s) + { + clist_remove(&s->n); + pulse_sink_input_remove(s); + } + else + DBG("Pulse: Removing sink which does not exist"); schedule_update(); } void pulse_sink_input_set_volume(int idx, pa_cvolume *cvol) { - PULSE_ASYNC_RUN(pa_context_set_sink_input_volume, pulse_ctx, idx, cvol, pulse_success_cb); + PULSE_ASYNC_RUN(pa_context_set_sink_input_volume, idx, cvol, pulse_success_cb); } void pulse_sink_input_set_mute(int idx, bool mute) { - PULSE_ASYNC_RUN(pa_context_set_sink_input_mute, pulse_ctx, idx, mute, pulse_success_cb); + PULSE_ASYNC_RUN(pa_context_set_sink_input_mute, idx, mute, pulse_success_cb); +} + +void pulse_sink_input_move(int input_idx, int sink_idx) +{ + PULSE_ASYNC_RUN(pa_context_move_sink_input_by_index, input_idx, sink_idx, pulse_success_cb); } /*** Sinks ***/ @@ -176,7 +221,7 @@ void pulse_sink_input_set_mute(int idx, bool mute) #define HASH_ZERO_FILL #include -static void pulse_sink_cb(pa_context *ctx, const pa_sink_info *i, int eol, void *userdata) +static void pulse_sink_cb(pa_context *ctx UNUSED, const pa_sink_info *i, int eol, void *userdata) { struct pulse_op *op = userdata; @@ -185,14 +230,15 @@ static void pulse_sink_cb(pa_context *ctx, const pa_sink_info *i, int eol, void if (op->is_init) { PULSE_STATE(PS_GET_SINK_INPUTS); - PULSE_ASYNC_INIT_RUN(pa_context_get_sink_input_info_list, ctx, pulse_sink_input_cb); + PULSE_ASYNC_INIT_RUN(pa_context_get_sink_input_info_list, pulse_sink_input_cb); } pulse_op_done(op); 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); @@ -203,6 +249,8 @@ static void pulse_sink_cb(pa_context *ctx, const pa_sink_info *i, int eol, void 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(); } @@ -223,14 +271,24 @@ struct pulse_sink *pulse_sink_by_name(const char *name) return NULL; } +struct pulse_sink *pulse_sink_by_idx(int idx) +{ + return pulse_sink_lookup(idx); +} + void pulse_sink_set_volume(int idx, pa_cvolume *cvol) { - PULSE_ASYNC_RUN(pa_context_set_sink_volume_by_index, pulse_ctx, idx, cvol, pulse_success_cb); + PULSE_ASYNC_RUN(pa_context_set_sink_volume_by_index, idx, cvol, pulse_success_cb); } void pulse_sink_set_mute(int idx, bool mute) { - PULSE_ASYNC_RUN(pa_context_set_sink_mute_by_index, pulse_ctx, idx, mute, pulse_success_cb); + 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 ***/ @@ -239,12 +297,13 @@ void pulse_sink_set_mute(int idx, bool mute) #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 #include -static void pulse_client_cb(pa_context *ctx, const pa_client_info *i, int eol, void *userdata) +static void pulse_client_cb(pa_context *ctx UNUSED, const pa_client_info *i, int eol, void *userdata) { struct pulse_op *op = userdata; @@ -253,7 +312,7 @@ static void pulse_client_cb(pa_context *ctx, const pa_client_info *i, int eol, v if (op->is_init) { PULSE_STATE(PS_GET_SINKS); - PULSE_ASYNC_INIT_RUN(pa_context_get_sink_info_list, ctx, pulse_sink_cb); + PULSE_ASYNC_INIT_RUN(pa_context_get_sink_info_list, pulse_sink_cb); } pulse_op_done(op); return; @@ -275,20 +334,23 @@ static void pulse_client_cb(pa_context *ctx, const pa_client_info *i, int eol, v 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 ***/ -static void pulse_subscribe_done_cb(pa_context *ctx, int success, void *userdata) +static void pulse_subscribe_done_cb(pa_context *ctx UNUSED, int success, void *userdata) { pulse_op_done(userdata); @@ -296,35 +358,39 @@ static void pulse_subscribe_done_cb(pa_context *ctx, int success, void *userdata msg(L_ERROR, "pa_context_subscribe failed: success=%d", success); PULSE_STATE(PS_GET_CLIENTS); - PULSE_ASYNC_INIT_RUN(pa_context_get_client_info_list, ctx, pulse_client_cb); + PULSE_ASYNC_INIT_RUN(pa_context_get_client_info_list, pulse_client_cb); } -static void pulse_event_cb(pa_context *ctx, pa_subscription_event_type_t type, uint32_t idx, void *userdata UNUSED) +static void pulse_event_cb(pa_context *ctx UNUSED, pa_subscription_event_type_t type, uint32_t idx, void *userdata UNUSED) { DBG("Pulse: SUBSCRIBE EVENT type=%08x idx=%u", type, idx); - uns object = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - uns action = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; + uint object = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; + uint action = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK; switch (object) { case PA_SUBSCRIPTION_EVENT_CLIENT: if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) - PULSE_ASYNC_RUN(pa_context_get_client_info, ctx, idx, pulse_client_cb); + PULSE_ASYNC_RUN(pa_context_get_client_info, idx, pulse_client_cb); else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) pulse_client_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, ctx, idx, pulse_sink_cb); + PULSE_ASYNC_RUN(pa_context_get_sink_info_by_index, idx, pulse_sink_cb); else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) pulse_sink_gone(idx); break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE) - PULSE_ASYNC_RUN(pa_context_get_sink_input_info, ctx, idx, pulse_sink_input_cb); + PULSE_ASYNC_RUN(pa_context_get_sink_input_info, idx, pulse_sink_input_cb); else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) pulse_sink_input_gone(idx); break; + case PA_SUBSCRIPTION_EVENT_SERVER: + if (action == PA_SUBSCRIPTION_EVENT_CHANGE) + PULSE_ASYNC_RUN(pa_context_get_server_info, pulse_server_cb); + break; } } @@ -348,7 +414,7 @@ static void pulse_state_cb(pa_context *ctx, void *userdata UNUSED) { PULSE_STATE(PS_SUBSCRIBE); pa_context_set_subscribe_callback(ctx, pulse_event_cb, NULL); - PULSE_ASYNC_INIT_RUN(pa_context_subscribe, ctx, PA_SUBSCRIPTION_MASK_ALL, pulse_subscribe_done_cb); + PULSE_ASYNC_INIT_RUN(pa_context_subscribe, PA_SUBSCRIPTION_MASK_ALL, pulse_subscribe_done_cb); } } else @@ -387,6 +453,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();