]> mj.ucw.cz Git - ursary.git/blob - pulse-ucw.c
Nocturn: Do not write to a missing USB device
[ursary.git] / pulse-ucw.c
1 /*
2  *      Glue between PulseAudio and LibUCW Mainloop
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef LOCAL_DEBUG
8
9 #include <ucw/lib.h>
10 #include <ucw/clists.h>
11 #include <ucw/mainloop.h>
12
13 #include <sys/poll.h>
14 #include <sys/time.h>
15
16 #include "ursaryd.h"
17
18 struct pmain_io {
19   cnode n;
20   struct main_file f;
21   clist io_events;
22 };
23
24 static clist pmain_io_list;
25
26 struct pa_io_event {
27   cnode n;
28   cnode gc_n;
29   struct pmain_io *io;
30   pa_io_event_flags_t events;
31   pa_io_event_cb_t callback;
32   pa_io_event_destroy_cb_t destroy_callback;
33   void *userdata;
34 };
35
36 static clist pmain_io_gc_list;
37
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);
42
43 struct pa_time_event {
44   cnode n;
45   struct main_timer t;
46   pa_time_event_cb_t callback;
47   pa_time_event_destroy_cb_t destroy_callback;
48   void *userdata;
49   struct timeval tv;
50 };
51
52 static clist pmain_time_gc_list;
53
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);
58
59 struct pa_defer_event {
60   cnode n;
61   struct main_hook h;
62   pa_defer_event_cb_t callback;
63   pa_defer_event_destroy_cb_t destroy_callback;
64   void *userdata;
65 };
66
67 static clist pmain_defer_gc_list;
68
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);
73
74 static void pmain_quit(pa_mainloop_api *a, int retval);
75
76 static struct main_hook pmain_gc_hook;
77
78 static void pmain_schedule_gc(void);
79
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,
85
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,
90
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,
95
96   .quit = pmain_quit,
97 };
98
99 static struct pmain_io *pmain_get_io(int fd)
100 {
101   CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list)
102     if (io->f.fd == fd)
103       {
104         DBG("Pulse: Recycling IO master");
105         return io;
106       }
107
108   struct pmain_io *io = xmalloc_zero(sizeof(*io));
109   io->f.fd = fd;
110   io->f.data = io;
111   clist_add_tail(&pmain_io_list, &io->n);
112   clist_init(&io->io_events);
113   return io;
114 }
115
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)
117 {
118   struct pa_io_event *e = xmalloc_zero(sizeof(*e));
119   DBG("Pulse: Creating new IO %p for fd %u", e, fd);
120
121   e->io = pmain_get_io(fd);
122   e->callback = cb;
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);
126   return e;
127 }
128
129 static int pmain_io_read(struct main_file *f)
130 {
131   struct pmain_io *io = f->data;
132   DBG("Pulse: fd %d ready for read", io->f.fd);
133
134   CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
135     if (e->events & PA_IO_EVENT_INPUT)
136       {
137         DBG("Pulse: Callback on IO %p", e);
138         e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata);
139       }
140
141   DBG("Pulse: fd %d read done", io->f.fd);
142   return HOOK_IDLE;
143 }
144
145 static int pmain_io_write(struct main_file *f)
146 {
147   struct pmain_io *io = f->data;
148   DBG("Pulse: fd %d ready for write", io->f.fd);
149
150   CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
151     if (e->events & PA_IO_EVENT_OUTPUT)
152       {
153         DBG("Pulse: Callback on IO %p", e);
154         e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata);
155       }
156
157   DBG("Pulse: fd %d write done", io->f.fd);
158   return HOOK_IDLE;
159 }
160
161 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events)
162 {
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);
165   e->events = events;
166
167   pa_io_event_flags_t mask = 0;
168   CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events)
169     mask |= f->events;
170   DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask);
171
172   if (mask)
173     {
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))
177         file_chg(&io->f);
178       else
179         file_add(&io->f);
180     }
181   else
182     file_del(&io->f);
183 }
184
185 static void pmain_io_free(pa_io_event *e)
186 {
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);
190   pmain_schedule_gc();
191 }
192
193 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb)
194 {
195   e->destroy_callback = cb;
196 }
197
198 static void pmain_time_handler(struct main_timer *t)
199 {
200   struct pa_time_event *e = t->data;
201   DBG("Pulse: Timer %p triggered", e);
202   timer_del(t);
203   e->callback(&pmain_api, e, &e->tv, e->userdata);
204   DBG("Pulse: Timer %p done", e);
205 }
206
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)
208 {
209   struct pa_time_event *e = xmalloc_zero(sizeof(*e));
210   DBG("Pulse: Creating timer %p", e);
211   e->callback = cb;
212   e->userdata = userdata;
213   e->t.handler = pmain_time_handler;
214   e->t.data = e;
215   pmain_time_restart(e, tv);
216   return e;
217 }
218
219 static timestamp_t timeval_to_timestamp(const struct timeval *tv)
220 {
221   return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000;
222 }
223
224 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv)
225 {
226   struct timeval now;
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);
232   timer_del(&e->t);
233   e->tv = *tv;
234   timer_add_rel(&e->t, ts_delta);
235 }
236
237 static void pmain_time_free(pa_time_event *e)
238 {
239   DBG("Pulse: Timer %p deleted", e);
240   timer_del(&e->t);
241   clist_add_tail(&pmain_time_gc_list, &e->n);
242   pmain_schedule_gc();
243 }
244
245 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
246 {
247   e->destroy_callback = cb;
248 }
249
250 static int pmain_defer_handler(struct main_hook *h)
251 {
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;
257 }
258
259 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata)
260 {
261   struct pa_defer_event *e = xmalloc_zero(sizeof(*e));
262   DBG("Pulse: Creating defer %p", e);
263   e->callback = cb;
264   e->userdata = userdata;
265   e->h.handler = pmain_defer_handler;
266   e->h.data = e;
267   pmain_defer_enable(e, 1);
268   return e;
269 }
270
271 static void pmain_defer_enable(pa_defer_event *e, int b)
272 {
273   DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e);
274   if (b)
275     hook_add(&e->h);
276   else
277     hook_del(&e->h);
278 }
279
280 static void pmain_defer_free(pa_defer_event *e)
281 {
282   DBG("Pulse: Deferred event %p deleted", e);
283   hook_del(&e->h);
284   clist_add_tail(&pmain_defer_gc_list, &e->n);
285   pmain_schedule_gc();
286 }
287
288 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb)
289 {
290   e->destroy_callback = cb;
291 }
292
293 static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED)
294 {
295   DBG("Pulse: Main loop quit not implemented");
296 }
297
298 static int pmain_gc_handler(struct main_hook *h)
299 {
300   DBG("Pulse: Garbage collector");
301   hook_del(h);
302
303   cnode *n;
304   while (n = clist_remove_head(&pmain_io_gc_list))
305     {
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))
313         {
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);
317           xfree(io);
318         }
319       xfree(ei);
320     }
321
322   struct pa_time_event *et;
323   while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list))
324     {
325       DBG("Pulse: GC for timer %p", et);
326       if (et->destroy_callback)
327         et->destroy_callback(&pmain_api, et, et->userdata);
328       xfree(et);
329     }
330
331   struct pa_defer_event *ed;
332   while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list))
333     {
334       DBG("Pulse: GC for defer %p", ed);
335       if (ed->destroy_callback)
336         ed->destroy_callback(&pmain_api, ed, ed->userdata);
337       xfree(ed);
338     }
339
340   DBG("Pulse: Garbage collector done");
341   return HOOK_RETRY;
342 }
343
344 static void pmain_schedule_gc(void)
345 {
346   hook_add(&pmain_gc_hook);
347 }
348
349 void pmain_init(void)
350 {
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;
356 }