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>
24 static clist pmain_io_list;
30 pa_io_event_flags_t events;
31 pa_io_event_cb_t callback;
32 pa_io_event_destroy_cb_t destroy_callback;
36 static clist pmain_io_gc_list;
38 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);
39 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events);
40 static void pmain_io_free(pa_io_event *e);
41 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb);
43 struct pa_time_event {
46 pa_time_event_cb_t callback;
47 pa_time_event_destroy_cb_t destroy_callback;
52 static clist pmain_time_gc_list;
54 static pa_time_event *pmain_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata);
55 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv);
56 static void pmain_time_free(pa_time_event *e);
57 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb);
59 struct pa_defer_event {
62 pa_defer_event_cb_t callback;
63 pa_defer_event_destroy_cb_t destroy_callback;
67 static clist pmain_defer_gc_list;
69 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata);
70 static void pmain_defer_enable(pa_defer_event *e, int b);
71 static void pmain_defer_free(pa_defer_event *e);
72 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb);
74 static void pmain_quit(pa_mainloop_api *a, int retval);
76 static struct main_hook pmain_gc_hook;
78 static void pmain_schedule_gc(void);
80 struct pa_mainloop_api pmain_api = {
81 .io_new = pmain_io_new,
82 .io_enable = pmain_io_enable,
83 .io_free = pmain_io_free,
84 .io_set_destroy = pmain_io_set_destroy,
86 .time_new = pmain_time_new,
87 .time_restart = pmain_time_restart,
88 .time_free = pmain_time_free,
89 .time_set_destroy = pmain_time_set_destroy,
91 .defer_new = pmain_defer_new,
92 .defer_enable = pmain_defer_enable,
93 .defer_free = pmain_defer_free,
94 .defer_set_destroy = pmain_defer_set_destroy,
99 static struct pmain_io *pmain_get_io(int fd)
101 CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list)
104 DBG("Pulse: Recycling IO master");
108 struct pmain_io *io = xmalloc_zero(sizeof(*io));
111 clist_add_tail(&pmain_io_list, &io->n);
112 clist_init(&io->io_events);
116 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)
118 struct pa_io_event *e = xmalloc_zero(sizeof(*e));
119 DBG("Pulse: Creating new IO %p for fd %u", e, fd);
121 e->io = pmain_get_io(fd);
123 e->userdata = userdata;
124 clist_add_head(&e->io->io_events, &e->n); // Do not call the new IO if created from another IO on the same fd
125 pmain_io_enable(e, events);
129 static int pmain_io_read(struct main_file *f)
131 struct pmain_io *io = f->data;
132 DBG("Pulse: fd %d ready for read", io->f.fd);
134 CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
135 if (e->events & PA_IO_EVENT_INPUT)
137 DBG("Pulse: Callback on IO %p", e);
138 e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata);
141 DBG("Pulse: fd %d read done", io->f.fd);
145 static int pmain_io_write(struct main_file *f)
147 struct pmain_io *io = f->data;
148 DBG("Pulse: fd %d ready for write", io->f.fd);
150 CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
151 if (e->events & PA_IO_EVENT_OUTPUT)
153 DBG("Pulse: Callback on IO %p", e);
154 e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata);
157 DBG("Pulse: fd %d write done", io->f.fd);
161 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events)
163 struct pmain_io *io = e->io;
164 DBG("Pulse: Changing IO event mask for IO %p on fd %d to %02x", e, io->f.fd, events);
167 pa_io_event_flags_t mask = 0;
168 CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events)
170 DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask);
174 io->f.read_handler = (mask & PA_IO_EVENT_INPUT) ? pmain_io_read : NULL;
175 io->f.write_handler = (mask & PA_IO_EVENT_OUTPUT) ? pmain_io_write : NULL;
176 if (file_is_active(&io->f))
185 static void pmain_io_free(pa_io_event *e)
187 DBG("Pulse: Deleting IO %p for fd %d", e, e->io->f.fd);
188 pmain_io_enable(e, 0);
189 clist_add_tail(&pmain_io_gc_list, &e->gc_n);
193 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb)
195 e->destroy_callback = cb;
198 static void pmain_time_handler(struct main_timer *t)
200 struct pa_time_event *e = t->data;
201 DBG("Pulse: Timer %p triggered", e);
203 e->callback(&pmain_api, e, &e->tv, e->userdata);
204 DBG("Pulse: Timer %p done", e);
207 static pa_time_event *pmain_time_new(pa_mainloop_api *api UNUSED, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
209 struct pa_time_event *e = xmalloc_zero(sizeof(*e));
210 DBG("Pulse: Creating timer %p", e);
212 e->userdata = userdata;
213 e->t.handler = pmain_time_handler;
215 pmain_time_restart(e, tv);
219 static timestamp_t timeval_to_timestamp(const struct timeval *tv)
221 return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000;
224 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv)
227 gettimeofday(&now, NULL);
228 timestamp_t ts_now = timeval_to_timestamp(&now);
229 timestamp_t ts_fire = timeval_to_timestamp(tv);
230 timestamp_t ts_delta = ts_fire - ts_now;
231 DBG("Pulse: Setting timer %p to %+d", e, (int) ts_delta);
234 timer_add_rel(&e->t, ts_delta);
237 static void pmain_time_free(pa_time_event *e)
239 DBG("Pulse: Timer %p deleted", e);
241 clist_add_tail(&pmain_time_gc_list, &e->n);
245 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
247 e->destroy_callback = cb;
250 static int pmain_defer_handler(struct main_hook *h)
252 struct pa_defer_event *e = h->data;
253 DBG("Pulse: Deferred event %p triggered", e);
254 e->callback(&pmain_api, e, e->userdata);
255 DBG("Pulse: Deferred event done");
256 return hook_is_active(&e->h) ? HOOK_RETRY : HOOK_IDLE;
259 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata)
261 struct pa_defer_event *e = xmalloc_zero(sizeof(*e));
262 DBG("Pulse: Creating defer %p", e);
264 e->userdata = userdata;
265 e->h.handler = pmain_defer_handler;
267 pmain_defer_enable(e, 1);
271 static void pmain_defer_enable(pa_defer_event *e, int b)
273 DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e);
280 static void pmain_defer_free(pa_defer_event *e)
282 DBG("Pulse: Deferred event %p deleted", e);
284 clist_add_tail(&pmain_defer_gc_list, &e->n);
288 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb)
290 e->destroy_callback = cb;
293 static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED)
295 DBG("Pulse: Main loop quit not implemented");
298 static int pmain_gc_handler(struct main_hook *h)
300 DBG("Pulse: Garbage collector");
304 while (n = clist_remove_head(&pmain_io_gc_list))
306 struct pa_io_event *ei = SKIP_BACK(struct pa_io_event, gc_n, n);
307 struct pmain_io *io = ei->io;
308 DBG("Pulse: GC of IO event %p on fd %d", ei, io->f.fd);
309 if (ei->destroy_callback)
310 ei->destroy_callback(&pmain_api, ei, ei->userdata);
311 clist_remove(&ei->n);
312 if (clist_empty(&io->io_events))
314 ASSERT(!file_is_active(&io->f));
315 DBG("Pulse: GC of IO master for fd %d", io->f.fd);
316 clist_remove(&io->n);
322 struct pa_time_event *et;
323 while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list))
325 DBG("Pulse: GC for timer %p", et);
326 if (et->destroy_callback)
327 et->destroy_callback(&pmain_api, et, et->userdata);
331 struct pa_defer_event *ed;
332 while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list))
334 DBG("Pulse: GC for defer %p", ed);
335 if (ed->destroy_callback)
336 ed->destroy_callback(&pmain_api, ed, ed->userdata);
340 DBG("Pulse: Garbage collector done");
344 static void pmain_schedule_gc(void)
346 hook_add(&pmain_gc_hook);
349 void pmain_init(void)
351 clist_init(&pmain_io_list);
352 clist_init(&pmain_io_gc_list);
353 clist_init(&pmain_time_gc_list);
354 clist_init(&pmain_defer_gc_list);
355 pmain_gc_hook.handler = pmain_gc_handler;