]> code.delx.au - pulseaudio/blob - polyp/glib-mainloop.c
Make the whole stuff LGPL only
[pulseaudio] / polyp / glib-mainloop.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
27
28 #include "glib-mainloop.h"
29 #include "idxset.h"
30 #include "xmalloc.h"
31
32 struct pa_io_event {
33 struct pa_glib_mainloop *mainloop;
34 int dead;
35 GIOChannel *io_channel;
36 GSource *source;
37 GIOCondition io_condition;
38 int fd;
39 void (*callback) (struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata);
40 void *userdata;
41 void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_io_event*e, void *userdata);
42 struct pa_io_event *next, *prev;
43 };
44
45 struct pa_time_event {
46 struct pa_glib_mainloop *mainloop;
47 int dead;
48 GSource *source;
49 struct timeval timeval;
50 void (*callback) (struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata);
51 void *userdata;
52 void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_time_event*e, void *userdata);
53 struct pa_time_event *next, *prev;
54 };
55
56 struct pa_defer_event {
57 struct pa_glib_mainloop *mainloop;
58 int dead;
59 GSource *source;
60 void (*callback) (struct pa_mainloop_api*m, struct pa_defer_event *e, void *userdata);
61 void *userdata;
62 void (*destroy_callback) (struct pa_mainloop_api *m, struct pa_defer_event*e, void *userdata);
63 struct pa_defer_event *next, *prev;
64 };
65
66 struct pa_glib_mainloop {
67 GMainContext *glib_main_context;
68 struct pa_mainloop_api api;
69 GSource *cleanup_source;
70 struct pa_io_event *io_events, *dead_io_events;
71 struct pa_time_event *time_events, *dead_time_events;
72 struct pa_defer_event *defer_events, *dead_defer_events;
73 };
74
75 static void schedule_free_dead_events(struct pa_glib_mainloop *g);
76
77 static void glib_io_enable(struct pa_io_event*e, enum pa_io_event_flags f);
78
79 static struct pa_io_event* glib_io_new(struct pa_mainloop_api*m, int fd, enum pa_io_event_flags f, void (*callback) (struct pa_mainloop_api*m, struct pa_io_event*e, int fd, enum pa_io_event_flags f, void *userdata), void *userdata) {
80 struct pa_io_event *e;
81 struct pa_glib_mainloop *g;
82
83 assert(m && m->userdata && fd >= 0 && callback);
84 g = m->userdata;
85
86 e = pa_xmalloc(sizeof(struct pa_io_event));
87 e->mainloop = m->userdata;
88 e->dead = 0;
89 e->fd = fd;
90 e->callback = callback;
91 e->userdata = userdata;
92 e->destroy_callback = NULL;
93
94 e->io_channel = g_io_channel_unix_new(e->fd);
95 assert(e->io_channel);
96 e->source = NULL;
97 e->io_condition = 0;
98
99 glib_io_enable(e, f);
100
101 e->next = g->io_events;
102 if (e->next) e->next->prev = e;
103 g->io_events = e;
104 e->prev = NULL;
105
106 return e;
107 }
108
109 static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) {
110 struct pa_io_event *e = data;
111 enum pa_io_event_flags f;
112 assert(source && e && e->io_channel == source);
113
114 f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) |
115 (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) |
116 (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) |
117 (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0);
118
119 e->callback(&e->mainloop->api, e, e->fd, f, e->userdata);
120 return TRUE;
121 }
122
123 static void glib_io_enable(struct pa_io_event*e, enum pa_io_event_flags f) {
124 GIOCondition c;
125 assert(e && !e->dead);
126
127 c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0);
128
129 if (c == e->io_condition)
130 return;
131
132 if (e->source) {
133 g_source_destroy(e->source);
134 g_source_unref(e->source);
135 }
136
137 e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP);
138 assert(e->source);
139
140 g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL);
141 g_source_attach(e->source, e->mainloop->glib_main_context);
142 g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
143
144 e->io_condition = c;
145 }
146
147 static void glib_io_free(struct pa_io_event*e) {
148 assert(e && !e->dead);
149
150 if (e->source) {
151 g_source_destroy(e->source);
152 g_source_unref(e->source);
153 e->source = NULL;
154 }
155
156 if (e->prev)
157 e->prev->next = e->next;
158 else
159 e->mainloop->io_events = e->next;
160
161 if (e->next)
162 e->next->prev = e->prev;
163
164 if ((e->next = e->mainloop->dead_io_events))
165 e->next->prev = e;
166
167 e->mainloop->dead_io_events = e;
168 e->prev = NULL;
169
170 e->dead = 1;
171 schedule_free_dead_events(e->mainloop);
172 }
173
174 static void glib_io_set_destroy(struct pa_io_event*e, void (*callback)(struct pa_mainloop_api*m, struct pa_io_event *e, void *userdata)) {
175 assert(e);
176 e->destroy_callback = callback;
177 }
178
179 /* Time sources */
180
181 static void glib_time_restart(struct pa_time_event*e, const struct timeval *tv);
182
183 static struct pa_time_event* glib_time_new(struct pa_mainloop_api*m, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*m, struct pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) {
184 struct pa_glib_mainloop *g;
185 struct pa_time_event *e;
186
187 assert(m && m->userdata && tv && callback);
188 g = m->userdata;
189
190 e = pa_xmalloc(sizeof(struct pa_time_event));
191 e->mainloop = g;
192 e->dead = 0;
193 e->callback = callback;
194 e->userdata = userdata;
195 e->destroy_callback = NULL;
196 e->source = NULL;
197
198 glib_time_restart(e, tv);
199
200 e->next = g->time_events;
201 if (e->next) e->next->prev = e;
202 g->time_events = e;
203 e->prev = NULL;
204
205 return e;
206 }
207
208 static guint msec_diff(const struct timeval *a, const struct timeval *b) {
209 guint r;
210 assert(a && b);
211
212 if (a->tv_sec < b->tv_sec)
213 return 0;
214
215 if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec)
216 return 0;
217
218 r = (a->tv_sec-b->tv_sec)*1000;
219
220 if (a->tv_usec >= b->tv_usec)
221 r += (a->tv_usec - b->tv_usec) / 1000;
222 else
223 r -= (b->tv_usec - a->tv_usec) / 1000;
224
225 return r;
226 }
227
228 static gboolean time_cb(gpointer data) {
229 struct pa_time_event* e = data;
230 assert(e && e->mainloop && e->source);
231
232 g_source_unref(e->source);
233 e->source = NULL;
234
235 e->callback(&e->mainloop->api, e, &e->timeval, e->userdata);
236 return FALSE;
237 }
238
239 static void glib_time_restart(struct pa_time_event*e, const struct timeval *tv) {
240 struct timeval now;
241 assert(e && e->mainloop && !e->dead);
242
243 gettimeofday(&now, NULL);
244 if (e->source) {
245 g_source_destroy(e->source);
246 g_source_unref(e->source);
247 }
248
249 if (tv) {
250 e->timeval = *tv;
251 e->source = g_timeout_source_new(msec_diff(tv, &now));
252 assert(e->source);
253 g_source_set_callback(e->source, time_cb, e, NULL);
254 g_source_set_priority(e->source, G_PRIORITY_DEFAULT);
255 g_source_attach(e->source, e->mainloop->glib_main_context);
256 } else
257 e->source = NULL;
258 }
259
260 static void glib_time_free(struct pa_time_event *e) {
261 assert(e && e->mainloop && !e->dead);
262
263 if (e->source) {
264 g_source_destroy(e->source);
265 g_source_unref(e->source);
266 e->source = NULL;
267 }
268
269 if (e->prev)
270 e->prev->next = e->next;
271 else
272 e->mainloop->time_events = e->next;
273
274 if (e->next)
275 e->next->prev = e->prev;
276
277 if ((e->next = e->mainloop->dead_time_events))
278 e->next->prev = e;
279
280 e->mainloop->dead_time_events = e;
281 e->prev = NULL;
282
283 e->dead = 1;
284 schedule_free_dead_events(e->mainloop);
285 }
286
287 static void glib_time_set_destroy(struct pa_time_event *e, void (*callback)(struct pa_mainloop_api*m, struct pa_time_event*e, void *userdata)) {
288 assert(e);
289 e->destroy_callback = callback;
290 }
291
292 /* Deferred sources */
293
294 static void glib_defer_enable(struct pa_defer_event *e, int b);
295
296 static struct pa_defer_event* glib_defer_new(struct pa_mainloop_api*m, void (*callback) (struct pa_mainloop_api*m, struct pa_defer_event *e, void *userdata), void *userdata) {
297 struct pa_defer_event *e;
298 struct pa_glib_mainloop *g;
299
300 assert(m && m->userdata && callback);
301 g = m->userdata;
302
303 e = pa_xmalloc(sizeof(struct pa_defer_event));
304 e->mainloop = g;
305 e->dead = 0;
306 e->callback = callback;
307 e->userdata = userdata;
308 e->destroy_callback = NULL;
309 e->source = NULL;
310
311 glib_defer_enable(e, 1);
312
313 e->next = g->defer_events;
314 if (e->next) e->next->prev = e;
315 g->defer_events = e;
316 e->prev = NULL;
317 return e;
318 }
319
320 static gboolean idle_cb(gpointer data) {
321 struct pa_defer_event* e = data;
322 assert(e && e->mainloop && e->source);
323
324 e->callback(&e->mainloop->api, e, e->userdata);
325 return TRUE;
326 }
327
328 static void glib_defer_enable(struct pa_defer_event *e, int b) {
329 assert(e && e->mainloop);
330
331 if (e->source && !b) {
332 g_source_destroy(e->source);
333 g_source_unref(e->source);
334 e->source = NULL;
335 } else if (!e->source && b) {
336 e->source = g_idle_source_new();
337 assert(e->source);
338 g_source_set_callback(e->source, idle_cb, e, NULL);
339 g_source_attach(e->source, e->mainloop->glib_main_context);
340 g_source_set_priority(e->source, G_PRIORITY_HIGH);
341 }
342 }
343
344 static void glib_defer_free(struct pa_defer_event *e) {
345 assert(e && e->mainloop && !e->dead);
346
347 if (e->source) {
348 g_source_destroy(e->source);
349 g_source_unref(e->source);
350 e->source = NULL;
351 }
352
353 if (e->prev)
354 e->prev->next = e->next;
355 else
356 e->mainloop->defer_events = e->next;
357
358 if (e->next)
359 e->next->prev = e->prev;
360
361 if ((e->next = e->mainloop->dead_defer_events))
362 e->next->prev = e;
363
364 e->mainloop->dead_defer_events = e;
365 e->prev = NULL;
366
367 e->dead = 1;
368 schedule_free_dead_events(e->mainloop);
369 }
370
371 static void glib_defer_set_destroy(struct pa_defer_event *e, void (*callback)(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata)) {
372 assert(e);
373 e->destroy_callback = callback;
374 }
375
376 /* quit() */
377
378 static void glib_quit(struct pa_mainloop_api*a, int retval) {
379 struct pa_glib_mainloop *g;
380 assert(a && a->userdata);
381 g = a->userdata;
382
383 /* NOOP */
384 }
385
386 static const struct pa_mainloop_api vtable = {
387 .userdata = NULL,
388
389 .io_new = glib_io_new,
390 .io_enable = glib_io_enable,
391 .io_free = glib_io_free,
392 .io_set_destroy= glib_io_set_destroy,
393
394 .time_new = glib_time_new,
395 .time_restart = glib_time_restart,
396 .time_free = glib_time_free,
397 .time_set_destroy = glib_time_set_destroy,
398
399 .defer_new = glib_defer_new,
400 .defer_enable = glib_defer_enable,
401 .defer_free = glib_defer_free,
402 .defer_set_destroy = glib_defer_set_destroy,
403
404 .quit = glib_quit,
405 };
406
407 struct pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) {
408 struct pa_glib_mainloop *g;
409
410 g = pa_xmalloc(sizeof(struct pa_glib_mainloop));
411 if (c) {
412 g->glib_main_context = c;
413 g_main_context_ref(c);
414 } else
415 g->glib_main_context = g_main_context_default();
416
417 g->api = vtable;
418 g->api.userdata = g;
419
420 g->io_events = g->dead_io_events = NULL;
421 g->time_events = g->dead_time_events = NULL;
422 g->defer_events = g->dead_defer_events = NULL;
423
424 g->cleanup_source = NULL;
425 return g;
426 }
427
428 static void free_io_events(struct pa_io_event *e) {
429 while (e) {
430 struct pa_io_event *r = e;
431 e = r->next;
432
433 if (r->source) {
434 g_source_destroy(r->source);
435 g_source_unref(r->source);
436 }
437
438 if (r->io_channel)
439 g_io_channel_unref(r->io_channel);
440
441 if (r->destroy_callback)
442 r->destroy_callback(&r->mainloop->api, r, r->userdata);
443
444 pa_xfree(r);
445 }
446 }
447
448 static void free_time_events(struct pa_time_event *e) {
449 while (e) {
450 struct pa_time_event *r = e;
451 e = r->next;
452
453 if (r->source) {
454 g_source_destroy(r->source);
455 g_source_unref(r->source);
456 }
457
458 if (r->destroy_callback)
459 r->destroy_callback(&r->mainloop->api, r, r->userdata);
460
461 pa_xfree(r);
462 }
463 }
464
465 static void free_defer_events(struct pa_defer_event *e) {
466 while (e) {
467 struct pa_defer_event *r = e;
468 e = r->next;
469
470 if (r->source) {
471 g_source_destroy(r->source);
472 g_source_unref(r->source);
473 }
474
475 if (r->destroy_callback)
476 r->destroy_callback(&r->mainloop->api, r, r->userdata);
477
478 pa_xfree(r);
479 }
480 }
481
482 void pa_glib_mainloop_free(struct pa_glib_mainloop* g) {
483 assert(g);
484
485 free_io_events(g->io_events);
486 free_io_events(g->dead_io_events);
487 free_defer_events(g->defer_events);
488 free_defer_events(g->dead_defer_events);
489 free_time_events(g->time_events);
490 free_time_events(g->dead_time_events);
491
492 if (g->cleanup_source) {
493 g_source_destroy(g->cleanup_source);
494 g_source_unref(g->cleanup_source);
495 }
496
497 g_main_context_unref(g->glib_main_context);
498 pa_xfree(g);
499 }
500
501 struct pa_mainloop_api* pa_glib_mainloop_get_api(struct pa_glib_mainloop *g) {
502 assert(g);
503 return &g->api;
504 }
505
506 static gboolean free_dead_events(gpointer p) {
507 struct pa_glib_mainloop *g = p;
508 assert(g);
509
510 free_io_events(g->dead_io_events);
511 free_defer_events(g->dead_defer_events);
512 free_time_events(g->dead_time_events);
513
514 g->dead_io_events = NULL;
515 g->dead_defer_events = NULL;
516 g->dead_time_events = NULL;
517
518 g_source_destroy(g->cleanup_source);
519 g_source_unref(g->cleanup_source);
520 g->cleanup_source = NULL;
521
522 return FALSE;
523 }
524
525 static void schedule_free_dead_events(struct pa_glib_mainloop *g) {
526 assert(g && g->glib_main_context);
527
528 if (g->cleanup_source)
529 return;
530
531 g->cleanup_source = g_idle_source_new();
532 assert(g->cleanup_source);
533 g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL);
534 g_source_attach(g->cleanup_source, g->glib_main_context);
535 }