From: Martin Mares Date: Sun, 9 Nov 2014 11:13:00 +0000 (+0100) Subject: More PA pieces X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=c04c5429272ba29283b6e8e50617bdc03e3f61c0;p=ursary.git More PA pieces --- diff --git a/Makefile b/Makefile index f1beb75..aaf7d56 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ LDLIBS=$(LIBUCW_LIBS) $(LIBUSB_LIBS) $(LIBPULSE_LIBS) all: ut -ut: ut.o +ut: ut.o pulse-ucw.o clean: rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*` diff --git a/pulse-ucw.c b/pulse-ucw.c new file mode 100644 index 0000000..b0581cb --- /dev/null +++ b/pulse-ucw.c @@ -0,0 +1,358 @@ +/* + * Glue between PulseAudio and LibUCW Mainloop + * + * (c) 2014 Martin Mares + */ + +#undef LOCAL_DEBUG + +#include +#include +#include + +#include +#include + +#include + +#include "ursaryd.h" + +struct pmain_io { + cnode n; + struct main_file f; + clist io_events; +}; + +static clist pmain_io_list; + +struct pa_io_event { + cnode n; + cnode gc_n; + struct pmain_io *io; + pa_io_event_flags_t events; + pa_io_event_cb_t callback; + pa_io_event_destroy_cb_t destroy_callback; + void *userdata; +}; + +static clist pmain_io_gc_list; + +static pa_io_event *pmain_io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata); +static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events); +static void pmain_io_free(pa_io_event *e); +static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb); + +struct pa_time_event { + cnode n; + struct main_timer t; + pa_time_event_cb_t callback; + pa_time_event_destroy_cb_t destroy_callback; + void *userdata; + struct timeval tv; +}; + +static clist pmain_time_gc_list; + +static pa_time_event *pmain_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata); +static void pmain_time_restart(pa_time_event *e, const struct timeval *tv); +static void pmain_time_free(pa_time_event *e); +static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb); + +struct pa_defer_event { + cnode n; + struct main_hook h; + pa_defer_event_cb_t callback; + pa_defer_event_destroy_cb_t destroy_callback; + void *userdata; +}; + +static clist pmain_defer_gc_list; + +static pa_defer_event *pmain_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata); +static void pmain_defer_enable(pa_defer_event *e, int b); +static void pmain_defer_free(pa_defer_event *e); +static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb); + +static void pmain_quit(pa_mainloop_api *a, int retval); + +static struct main_hook pmain_gc_hook; + +static void pmain_trigger_gc(void); + +struct pa_mainloop_api pmain_api = { + .io_new = pmain_io_new, + .io_enable = pmain_io_enable, + .io_free = pmain_io_free, + .io_set_destroy = pmain_io_set_destroy, + + .time_new = pmain_time_new, + .time_restart = pmain_time_restart, + .time_free = pmain_time_free, + .time_set_destroy = pmain_time_set_destroy, + + .defer_new = pmain_defer_new, + .defer_enable = pmain_defer_enable, + .defer_free = pmain_defer_free, + .defer_set_destroy = pmain_defer_set_destroy, + + .quit = pmain_quit, +}; + +static struct pmain_io *pmain_get_io(int fd) +{ + CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list) + if (io->f.fd == fd) + { + DBG("Pulse: Recycling IO master"); + return io; + } + + struct pmain_io *io = xmalloc(sizeof(*io)); + io->f.fd = fd; + io->f.data = io; + clist_add_tail(&pmain_io_list, &io->n); + clist_init(&io->io_events); + return io; +} + +static pa_io_event *pmain_io_new(pa_mainloop_api *api UNUSED, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) +{ + struct pa_io_event *e = xmalloc_zero(sizeof(*e)); + DBG("Pulse: Creating new IO %p for fd %u", e, fd); + + e->io = pmain_get_io(fd); + e->callback = cb; + e->userdata = userdata; + clist_add_head(&e->io->io_events, &e->n); // Do not call the new IO if created from another IO on the same fd + pmain_io_enable(e, events); + return e; +} + +static int pmain_io_read(struct main_file *f) +{ + struct pmain_io *io = f->data; + DBG("Pulse: fd %d ready for read", io->f.fd); + + CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events) + if (e->events & PA_IO_EVENT_INPUT) + { + DBG("Pulse: Callback on IO %p", e); + e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata); + } + + DBG("Pulse: fd %d read done", io->f.fd); + return HOOK_IDLE; +} + +static int pmain_io_write(struct main_file *f) +{ + struct pmain_io *io = f->data; + DBG("Pulse: fd %d ready for write", io->f.fd); + + CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events) + if (e->events & PA_IO_EVENT_OUTPUT) + { + DBG("Pulse: Callback on IO %p", e); + e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata); + } + + DBG("Pulse: fd %d write done", io->f.fd); + return HOOK_IDLE; +} + +static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events) +{ + struct pmain_io *io = e->io; + DBG("Pulse: Changing IO event mask for IO %p on fd %d to %02x", e, io->f.fd, events); + e->events = events; + + pa_io_event_flags_t mask = 0; + CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events) + mask |= f->events; + DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask); + + if (mask) + { + io->f.read_handler = (mask & PA_IO_EVENT_INPUT) ? pmain_io_read : NULL; + io->f.write_handler = (mask & PA_IO_EVENT_OUTPUT) ? pmain_io_write : NULL; + if (file_is_active(&io->f)) + file_chg(&io->f); + else + file_add(&io->f); + } + else + file_del(&io->f); +} + +static void pmain_io_free(pa_io_event *e) +{ + DBG("Pulse: Deleting IO %p for fd %d", e, e->io->f.fd); + pmain_io_enable(e, 0); + clist_add_tail(&pmain_io_gc_list, &e->gc_n); + pmain_trigger_gc(); +} + +static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) +{ + e->destroy_callback = cb; +} + +static void pmain_time_handler(struct main_timer *t) +{ + struct pa_time_event *e = t->data; + DBG("Pulse: Timer %p triggered", e); + timer_del(t); + e->callback(&pmain_api, e, &e->tv, e->userdata); + DBG("Pulse: Timer %p done", e); +} + +static pa_time_event *pmain_time_new(pa_mainloop_api *api UNUSED, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata) +{ + struct pa_time_event *e = xmalloc_zero(sizeof(*e)); + DBG("Pulse: Creating timer %p", e); + e->callback = cb; + e->userdata = userdata; + e->t.handler = pmain_time_handler; + e->t.data = e; + pmain_time_restart(e, tv); + return e; +} + +static timestamp_t timeval_to_timestamp(const struct timeval *tv) +{ + return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000; +} + +static void pmain_time_restart(pa_time_event *e, const struct timeval *tv) +{ + struct timeval now; + gettimeofday(&now, NULL); + timestamp_t ts_now = timeval_to_timestamp(&now); + timestamp_t ts_fire = timeval_to_timestamp(tv); + timestamp_t ts_delta = ts_fire - ts_now; + DBG("Pulse: Setting timer %p to %+d", e, (int) ts_delta); + timer_del(&e->t); + e->tv = *tv; + timer_add_rel(&e->t, ts_delta); +} + +static void pmain_time_free(pa_time_event *e) +{ + DBG("Pulse: Timer %p deleted", e); + timer_del(&e->t); + clist_add_tail(&pmain_time_gc_list, &e->n); + pmain_trigger_gc(); +} + +static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) +{ + e->destroy_callback = cb; +} + +static int pmain_defer_handler(struct main_hook *h) +{ + struct pa_defer_event *e = h->data; + DBG("Pulse: Deferred event %p triggered", e); + e->callback(&pmain_api, e, e->userdata); + DBG("Pulse: Deferred event done"); + return hook_is_active(&e->h) ? HOOK_RETRY : HOOK_IDLE; +} + +static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata) +{ + struct pa_defer_event *e = xmalloc_zero(sizeof(*e)); + DBG("Pulse: Creating defer %p", e); + e->callback = cb; + e->userdata = userdata; + e->h.handler = pmain_defer_handler; + e->h.data = e; + pmain_defer_enable(e, 1); + return e; +} + +static void pmain_defer_enable(pa_defer_event *e, int b) +{ + DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e); + if (b) + hook_add(&e->h); + else + hook_del(&e->h); +} + +static void pmain_defer_free(pa_defer_event *e) +{ + DBG("Pulse: Deferred event %p deleted", e); + hook_del(&e->h); + clist_add_tail(&pmain_defer_gc_list, &e->n); + pmain_trigger_gc(); +} + +static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) +{ + e->destroy_callback = cb; +} + +static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED) +{ + DBG("Pulse: Main loop quit not implemented"); +} + +static int pmain_gc_handler(struct main_hook *h) +{ + DBG("Pulse: Garbage collector"); + hook_del(h); + + cnode *n; + while (n = clist_remove_head(&pmain_io_gc_list)) + { + struct pa_io_event *ei = SKIP_BACK(struct pa_io_event, gc_n, n); + struct pmain_io *io = ei->io; + DBG("Pulse: GC of IO event %p on fd %d", ei, io->f.fd); + if (ei->destroy_callback) + ei->destroy_callback(&pmain_api, ei, ei->userdata); + clist_remove(&ei->n); + if (clist_empty(&io->io_events)) + { + ASSERT(!file_is_active(&io->f)); + DBG("Pulse: GC of IO master for fd %d", io->f.fd); + clist_remove(&io->n); + xfree(io); + } + xfree(ei); + } + + struct pa_time_event *et; + while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list)) + { + DBG("Pulse: GC for timer %p", et); + if (et->destroy_callback) + et->destroy_callback(&pmain_api, et, et->userdata); + xfree(et); + } + + struct pa_defer_event *ed; + while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list)) + { + DBG("Pulse: GC for defer %p", ed); + if (ed->destroy_callback) + ed->destroy_callback(&pmain_api, ed, ed->userdata); + xfree(ed); + } + + DBG("Pulse: Garbage collector done"); + return HOOK_RETRY; +} + +static void pmain_trigger_gc(void) +{ + hook_add(&pmain_gc_hook); +} + +void pmain_init(void) +{ + clist_init(&pmain_io_list); + clist_init(&pmain_io_gc_list); + clist_init(&pmain_time_gc_list); + clist_init(&pmain_defer_gc_list); + pmain_gc_hook.handler = pmain_gc_handler; +} diff --git a/ut.c b/ut.c index afb7089..8cb2a92 100644 --- a/ut.c +++ b/ut.c @@ -16,6 +16,8 @@ #include #include +#include "ursaryd.h" + /* * Interface to Novation Nocturn * @@ -373,349 +375,147 @@ static void usb_init(void) * FIXME */ -struct pmain_io { - cnode n; - struct main_file f; - clist io_events; -}; - -static clist pmain_io_list; - -struct pa_io_event { - cnode n; - cnode gc_n; - struct pmain_io *io; - pa_io_event_flags_t events; - pa_io_event_cb_t callback; - pa_io_event_destroy_cb_t destroy_callback; - void *userdata; -}; - -static clist pmain_io_gc_list; - -static pa_io_event *pmain_io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata); -static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events); -static void pmain_io_free(pa_io_event *e); -static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb); +static pa_context *pulse_ctx; -struct pa_time_event { - cnode n; - struct main_timer t; - pa_time_event_cb_t callback; - pa_time_event_destroy_cb_t destroy_callback; - void *userdata; - struct timeval tv; +enum pulse_state { + PS_OFFLINE, + PS_SUBSCRIBE, + PS_GET_CLIENTS, + PS_GET_SINKS, + PS_GET_SINK_INPUTS, + PS_ONLINE, }; -static clist pmain_time_gc_list; - -static pa_time_event *pmain_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata); -static void pmain_time_restart(pa_time_event *e, const struct timeval *tv); -static void pmain_time_free(pa_time_event *e); -static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb); +static enum pulse_state pulse_state; +#define PULSE_STATE(s) do { pulse_state = s; DBG("Pulse: " #s); } while (0) -struct pa_defer_event { +// Tracking of currently running asynchronous operations +struct pulse_op { cnode n; - struct main_hook h; - pa_defer_event_cb_t callback; - pa_defer_event_destroy_cb_t destroy_callback; - void *userdata; + pa_operation *o; + bool is_init; }; -static clist pmain_defer_gc_list; - -static pa_defer_event *pmain_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata); -static void pmain_defer_enable(pa_defer_event *e, int b); -static void pmain_defer_free(pa_defer_event *e); -static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb); +static clist pulse_op_list; -static void pmain_quit(pa_mainloop_api *a, int retval); - -static struct main_hook pmain_gc_hook; - -static void pmain_trigger_gc(void); - -static struct pa_mainloop_api pmainloop_api = { - .io_new = pmain_io_new, - .io_enable = pmain_io_enable, - .io_free = pmain_io_free, - .io_set_destroy = pmain_io_set_destroy, - - .time_new = pmain_time_new, - .time_restart = pmain_time_restart, - .time_free = pmain_time_free, - .time_set_destroy = pmain_time_set_destroy, - - .defer_new = pmain_defer_new, - .defer_enable = pmain_defer_enable, - .defer_free = pmain_defer_free, - .defer_set_destroy = pmain_defer_set_destroy, - - .quit = pmain_quit, -}; - -static struct pmain_io *pmain_get_io(int fd) +static struct pulse_op *pulse_op_new(void) { - CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list) - if (io->f.fd == fd) - { - DBG("Pulse: Recycling IO master"); - return io; - } - - struct pmain_io *io = xmalloc(sizeof(*io)); - io->f.fd = fd; - io->f.data = io; - clist_add_tail(&pmain_io_list, &io->n); - clist_init(&io->io_events); - return io; + struct pulse_op *op = xmalloc_zero(sizeof(*op)); + clist_add_tail(&pulse_op_list, &op->n); + return op; } -static pa_io_event *pmain_io_new(pa_mainloop_api *api UNUSED, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) +static void pulse_op_done(struct pulse_op *op) { - struct pa_io_event *e = xmalloc_zero(sizeof(*e)); - DBG("Pulse: Creating new IO %p for fd %u", e, fd); - - e->io = pmain_get_io(fd); - e->callback = cb; - e->userdata = userdata; - clist_add_head(&e->io->io_events, &e->n); // Do not call the new IO if created from another IO on the same fd - pmain_io_enable(e, events); - return e; + if (op->o) + pa_operation_unref(op->o); + clist_remove(&op->n); + xfree(op); } -static int pmain_io_read(struct main_file *f) +static void pulse_op_cancel_all(void) { - struct pmain_io *io = f->data; - DBG("Pulse: fd %d ready for read", io->f.fd); - - CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events) - if (e->events & PA_IO_EVENT_INPUT) - { - DBG("Pulse: Callback on IO %p", e); - e->callback(&pmainloop_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata); - } - - DBG("Pulse: fd %d read done", io->f.fd); - return HOOK_IDLE; -} - -static int pmain_io_write(struct main_file *f) -{ - struct pmain_io *io = f->data; - DBG("Pulse: fd %d ready for write", io->f.fd); - - CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events) - if (e->events & PA_IO_EVENT_OUTPUT) - { - DBG("Pulse: Callback on IO %p", e); - e->callback(&pmainloop_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata); - } - - DBG("Pulse: fd %d write done", io->f.fd); - return HOOK_IDLE; -} - -static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events) -{ - struct pmain_io *io = e->io; - DBG("Pulse: Changing IO event mask for IO %p on fd %d to %02x", e, io->f.fd, events); - e->events = events; - - pa_io_event_flags_t mask = 0; - CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events) - mask |= f->events; - DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask); - - if (mask) + struct pulse_op *op; + while (op = (struct pulse_op *) clist_head(&pulse_op_list)) { - io->f.read_handler = (mask & PA_IO_EVENT_INPUT) ? pmain_io_read : NULL; - io->f.write_handler = (mask & PA_IO_EVENT_OUTPUT) ? pmain_io_write : NULL; - if (file_is_active(&io->f)) - file_chg(&io->f); - else - file_add(&io->f); + DBG("Pulse: Cancelling pending operation"); + pa_operation_cancel(op->o); + pulse_op_done(op); } - else - file_del(&io->f); -} - -static void pmain_io_free(pa_io_event *e) -{ - DBG("Pulse: Deleting IO %p for fd %d", e, e->io->f.fd); - pmain_io_enable(e, 0); - clist_add_tail(&pmain_io_gc_list, &e->gc_n); - pmain_trigger_gc(); -} - -static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) -{ - e->destroy_callback = cb; -} - -static void pmain_time_handler(struct main_timer *t) -{ - struct pa_time_event *e = t->data; - DBG("Pulse: Timer %p triggered", e); - timer_del(t); - e->callback(&pmainloop_api, e, &e->tv, e->userdata); - DBG("Pulse: Timer %p done", e); -} - -static pa_time_event *pmain_time_new(pa_mainloop_api *api UNUSED, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata) -{ - struct pa_time_event *e = xmalloc_zero(sizeof(*e)); - DBG("Pulse: Creating timer %p", e); - e->callback = cb; - e->userdata = userdata; - e->t.handler = pmain_time_handler; - e->t.data = e; - pmain_time_restart(e, tv); - return e; } -static timestamp_t timeval_to_timestamp(const struct timeval *tv) -{ - return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000; -} +#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) -static void pmain_time_restart(pa_time_event *e, const struct timeval *tv) +static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info *i, int eol, void *userdata) { - struct timeval now; - gettimeofday(&now, NULL); - timestamp_t ts_now = timeval_to_timestamp(&now); - timestamp_t ts_fire = timeval_to_timestamp(tv); - timestamp_t ts_delta = ts_fire - ts_now; - DBG("Pulse: Setting timer %p to %+d", e, (int) ts_delta); - timer_del(&e->t); - e->tv = *tv; - timer_add_rel(&e->t, ts_delta); -} + struct pulse_op *op = userdata; -static void pmain_time_free(pa_time_event *e) -{ - DBG("Pulse: Timer %p deleted", e); - timer_del(&e->t); - clist_add_tail(&pmain_time_gc_list, &e->n); - pmain_trigger_gc(); -} - -static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb) -{ - e->destroy_callback = cb; -} - -static int pmain_defer_handler(struct main_hook *h) -{ - struct pa_defer_event *e = h->data; - DBG("Pulse: Deferred event %p triggered", e); - e->callback(&pmainloop_api, e, e->userdata); - DBG("Pulse: Deferred event done"); - return hook_is_active(&e->h) ? HOOK_RETRY : HOOK_IDLE; -} - -static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata) -{ - struct pa_defer_event *e = xmalloc_zero(sizeof(*e)); - DBG("Pulse: Creating defer %p", e); - e->callback = cb; - e->userdata = userdata; - e->h.handler = pmain_defer_handler; - e->h.data = e; - pmain_defer_enable(e, 1); - return e; -} + if (eol) + { + if (op->is_init) + PULSE_STATE(PS_ONLINE); + pulse_op_done(op); + return; + } -static void pmain_defer_enable(pa_defer_event *e, int b) -{ - DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e); - if (b) - hook_add(&e->h); - else - hook_del(&e->h); + DBG("Pulse: SINK INPUT #%u: %s client=%d sink=%d", i->index, i->name, i->client, i->sink); } -static void pmain_defer_free(pa_defer_event *e) +static void pulse_sink_cb(pa_context *ctx, const pa_sink_info *i, int eol, void *userdata) { - DBG("Pulse: Deferred event %p deleted", e); - hook_del(&e->h); - clist_add_tail(&pmain_defer_gc_list, &e->n); - pmain_trigger_gc(); -} + struct pulse_op *op = userdata; -static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb) -{ - e->destroy_callback = cb; -} + if (eol) + { + 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_op_done(op); + return; + } -static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED) -{ - DBG("Pulse: Main loop quit not implemented"); + DBG("Pulse: SINK #%u: %s (%s)", i->index, i->name, i->description); } -static int pmain_gc_handler(struct main_hook *h) +static void pulse_client_cb(pa_context *ctx, const pa_client_info *i, int eol, void *userdata) { - DBG("Pulse: Garbage collector"); - hook_del(h); + struct pulse_op *op = userdata; - cnode *n; - while (n = clist_remove_head(&pmain_io_gc_list)) + if (eol) { - struct pa_io_event *ei = SKIP_BACK(struct pa_io_event, gc_n, n); - struct pmain_io *io = ei->io; - DBG("Pulse: GC of IO event %p on fd %d", ei, io->f.fd); - if (ei->destroy_callback) - ei->destroy_callback(&pmainloop_api, ei, ei->userdata); - clist_remove(&ei->n); - if (clist_empty(&io->io_events)) + if (op->is_init) { - ASSERT(!file_is_active(&io->f)); - DBG("Pulse: GC of IO master for fd %d", io->f.fd); - clist_remove(&io->n); - xfree(io); + PULSE_STATE(PS_GET_SINKS); + PULSE_ASYNC_INIT_RUN(pa_context_get_sink_info_list, ctx, pulse_sink_cb); } - xfree(ei); - } - - struct pa_time_event *et; - while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list)) - { - DBG("Pulse: GC for timer %p", et); - if (et->destroy_callback) - et->destroy_callback(&pmainloop_api, et, et->userdata); - xfree(et); - } - - struct pa_defer_event *ed; - while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list)) - { - DBG("Pulse: GC for defer %p", ed); - if (ed->destroy_callback) - ed->destroy_callback(&pmainloop_api, ed, ed->userdata); - xfree(ed); + pulse_op_done(op); + return; } - DBG("Pulse: Garbage collector done"); - return HOOK_RETRY; + DBG("Pulse: CLIENT #%u: %s mod=%u drv=%s", i->index, i->name, i->owner_module, i->driver); } -static void pmain_trigger_gc(void) +static void pulse_subscribe_done_cb(pa_context *ctx, int success, void *userdata) { - hook_add(&pmain_gc_hook); -} + pulse_op_done(userdata); -static pa_context *pulse_ctx; -static bool pulse_ready; + if (!success) + 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); +} -static void pulse_client_info_cb(pa_context *ctx, const pa_client_info *i, int eol, void *userdata) +static void pulse_event_cb(pa_context *ctx, pa_subscription_event_type_t type, uint32_t idx, void *userdata UNUSED) { - if (eol) + 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; + switch (object) { - DBG("Pulse: CLIENT DONE"); - return; + 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); + else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) + DBG("Pulse: REMOVE CLIENT #%u", 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); + else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) + DBG("Pulse: REMOVE SINK #%u", 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); + else if (action == PA_SUBSCRIPTION_EVENT_REMOVE) + DBG("Pulse: REMOVE SINK INPUT #%u", idx); + break; } - - DBG("Pulse: CLIENT #%u: %s mod=%u drv=%s", i->index, i->name, i->owner_module, i->driver); } static void pulse_state_cb(pa_context *ctx, void *userdata UNUSED) @@ -724,33 +524,28 @@ static void pulse_state_cb(pa_context *ctx, void *userdata UNUSED) DBG("Pulse: State callback, new state = %d", state); if (state == PA_CONTEXT_READY) { - if (!pulse_ready) + if (pulse_state == PS_OFFLINE) { - pulse_ready = 1; - DBG("Pulse: ONLINE"); - pa_context_get_client_info_list(ctx, pulse_client_info_cb, NULL); - // FIXME: Discard the operation when server goes offline + 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); } } else { - if (pulse_ready) + if (pulse_state != PS_OFFLINE) { - pulse_ready = 0; - DBG("Pulse: OFFLINE"); + PULSE_STATE(PS_OFFLINE); + pulse_op_cancel_all(); } } } static void pulse_init(void) { - clist_init(&pmain_io_list); - clist_init(&pmain_io_gc_list); - clist_init(&pmain_time_gc_list); - clist_init(&pmain_defer_gc_list); - pmain_gc_hook.handler = pmain_gc_handler; - - pulse_ctx = pa_context_new(&pmainloop_api, "ursaryd"); + pmain_init(); + clist_init(&pulse_op_list); + pulse_ctx = pa_context_new(&pmain_api, "ursaryd"); pa_context_set_state_callback(pulse_ctx, pulse_state_cb, NULL); pa_context_connect(pulse_ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); }