2 * Glue between PulseAudio and LibUCW Mainloop
4 * (c) 2014 Martin Mares <mj@ucw.cz>
10 #include <ucw/clists.h>
11 #include <ucw/mainloop.h>
16 #include <pulse/pulseaudio.h>
26 static clist pmain_io_list;
32 pa_io_event_flags_t events;
33 pa_io_event_cb_t callback;
34 pa_io_event_destroy_cb_t destroy_callback;
38 static clist pmain_io_gc_list;
40 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);
41 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events);
42 static void pmain_io_free(pa_io_event *e);
43 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb);
45 struct pa_time_event {
48 pa_time_event_cb_t callback;
49 pa_time_event_destroy_cb_t destroy_callback;
54 static clist pmain_time_gc_list;
56 static pa_time_event *pmain_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata);
57 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv);
58 static void pmain_time_free(pa_time_event *e);
59 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb);
61 struct pa_defer_event {
64 pa_defer_event_cb_t callback;
65 pa_defer_event_destroy_cb_t destroy_callback;
69 static clist pmain_defer_gc_list;
71 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata);
72 static void pmain_defer_enable(pa_defer_event *e, int b);
73 static void pmain_defer_free(pa_defer_event *e);
74 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb);
76 static void pmain_quit(pa_mainloop_api *a, int retval);
78 static struct main_hook pmain_gc_hook;
80 static void pmain_schedule_gc(void);
82 struct pa_mainloop_api pmain_api = {
83 .io_new = pmain_io_new,
84 .io_enable = pmain_io_enable,
85 .io_free = pmain_io_free,
86 .io_set_destroy = pmain_io_set_destroy,
88 .time_new = pmain_time_new,
89 .time_restart = pmain_time_restart,
90 .time_free = pmain_time_free,
91 .time_set_destroy = pmain_time_set_destroy,
93 .defer_new = pmain_defer_new,
94 .defer_enable = pmain_defer_enable,
95 .defer_free = pmain_defer_free,
96 .defer_set_destroy = pmain_defer_set_destroy,
101 static struct pmain_io *pmain_get_io(int fd)
103 CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list)
106 DBG("Pulse: Recycling IO master");
110 struct pmain_io *io = xmalloc_zero(sizeof(*io));
113 clist_add_tail(&pmain_io_list, &io->n);
114 clist_init(&io->io_events);
118 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)
120 struct pa_io_event *e = xmalloc_zero(sizeof(*e));
121 DBG("Pulse: Creating new IO %p for fd %u", e, fd);
123 e->io = pmain_get_io(fd);
125 e->userdata = userdata;
126 clist_add_head(&e->io->io_events, &e->n); // Do not call the new IO if created from another IO on the same fd
127 pmain_io_enable(e, events);
131 static int pmain_io_read(struct main_file *f)
133 struct pmain_io *io = f->data;
134 DBG("Pulse: fd %d ready for read", io->f.fd);
136 CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
137 if (e->events & PA_IO_EVENT_INPUT)
139 DBG("Pulse: Callback on IO %p", e);
140 e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata);
143 DBG("Pulse: fd %d read done", io->f.fd);
147 static int pmain_io_write(struct main_file *f)
149 struct pmain_io *io = f->data;
150 DBG("Pulse: fd %d ready for write", io->f.fd);
152 CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
153 if (e->events & PA_IO_EVENT_OUTPUT)
155 DBG("Pulse: Callback on IO %p", e);
156 e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata);
159 DBG("Pulse: fd %d write done", io->f.fd);
163 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events)
165 struct pmain_io *io = e->io;
166 DBG("Pulse: Changing IO event mask for IO %p on fd %d to %02x", e, io->f.fd, events);
169 pa_io_event_flags_t mask = 0;
170 CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events)
172 DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask);
176 io->f.read_handler = (mask & PA_IO_EVENT_INPUT) ? pmain_io_read : NULL;
177 io->f.write_handler = (mask & PA_IO_EVENT_OUTPUT) ? pmain_io_write : NULL;
178 if (file_is_active(&io->f))
187 static void pmain_io_free(pa_io_event *e)
189 DBG("Pulse: Deleting IO %p for fd %d", e, e->io->f.fd);
190 pmain_io_enable(e, 0);
191 clist_add_tail(&pmain_io_gc_list, &e->gc_n);
195 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb)
197 e->destroy_callback = cb;
200 static void pmain_time_handler(struct main_timer *t)
202 struct pa_time_event *e = t->data;
203 DBG("Pulse: Timer %p triggered", e);
205 e->callback(&pmain_api, e, &e->tv, e->userdata);
206 DBG("Pulse: Timer %p done", e);
209 static pa_time_event *pmain_time_new(pa_mainloop_api *api UNUSED, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
211 struct pa_time_event *e = xmalloc_zero(sizeof(*e));
212 DBG("Pulse: Creating timer %p", e);
214 e->userdata = userdata;
215 e->t.handler = pmain_time_handler;
217 pmain_time_restart(e, tv);
221 static timestamp_t timeval_to_timestamp(const struct timeval *tv)
223 return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000;
226 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv)
229 gettimeofday(&now, NULL);
230 timestamp_t ts_now = timeval_to_timestamp(&now);
231 timestamp_t ts_fire = timeval_to_timestamp(tv);
232 timestamp_t ts_delta = ts_fire - ts_now;
233 DBG("Pulse: Setting timer %p to %+d", e, (int) ts_delta);
236 timer_add_rel(&e->t, ts_delta);
239 static void pmain_time_free(pa_time_event *e)
241 DBG("Pulse: Timer %p deleted", e);
243 clist_add_tail(&pmain_time_gc_list, &e->n);
247 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
249 e->destroy_callback = cb;
252 static int pmain_defer_handler(struct main_hook *h)
254 struct pa_defer_event *e = h->data;
255 DBG("Pulse: Deferred event %p triggered", e);
256 e->callback(&pmain_api, e, e->userdata);
257 DBG("Pulse: Deferred event done");
258 return hook_is_active(&e->h) ? HOOK_RETRY : HOOK_IDLE;
261 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata)
263 struct pa_defer_event *e = xmalloc_zero(sizeof(*e));
264 DBG("Pulse: Creating defer %p", e);
266 e->userdata = userdata;
267 e->h.handler = pmain_defer_handler;
269 pmain_defer_enable(e, 1);
273 static void pmain_defer_enable(pa_defer_event *e, int b)
275 DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e);
282 static void pmain_defer_free(pa_defer_event *e)
284 DBG("Pulse: Deferred event %p deleted", e);
286 clist_add_tail(&pmain_defer_gc_list, &e->n);
290 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb)
292 e->destroy_callback = cb;
295 static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED)
297 DBG("Pulse: Main loop quit not implemented");
300 static int pmain_gc_handler(struct main_hook *h)
302 DBG("Pulse: Garbage collector");
306 while (n = clist_remove_head(&pmain_io_gc_list))
308 struct pa_io_event *ei = SKIP_BACK(struct pa_io_event, gc_n, n);
309 struct pmain_io *io = ei->io;
310 DBG("Pulse: GC of IO event %p on fd %d", ei, io->f.fd);
311 if (ei->destroy_callback)
312 ei->destroy_callback(&pmain_api, ei, ei->userdata);
313 clist_remove(&ei->n);
314 if (clist_empty(&io->io_events))
316 ASSERT(!file_is_active(&io->f));
317 DBG("Pulse: GC of IO master for fd %d", io->f.fd);
318 clist_remove(&io->n);
324 struct pa_time_event *et;
325 while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list))
327 DBG("Pulse: GC for timer %p", et);
328 if (et->destroy_callback)
329 et->destroy_callback(&pmain_api, et, et->userdata);
333 struct pa_defer_event *ed;
334 while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list))
336 DBG("Pulse: GC for defer %p", ed);
337 if (ed->destroy_callback)
338 ed->destroy_callback(&pmain_api, ed, ed->userdata);
342 DBG("Pulse: Garbage collector done");
346 static void pmain_schedule_gc(void)
348 hook_add(&pmain_gc_hook);
351 void pmain_init(void)
353 clist_init(&pmain_io_list);
354 clist_init(&pmain_io_gc_list);
355 clist_init(&pmain_time_gc_list);
356 clist_init(&pmain_defer_gc_list);
357 pmain_gc_hook.handler = pmain_gc_handler;