source: roaraudio/libroarpulse/mainloop.c @ 4707:9f23480cf23e

Last change on this file since 4707:9f23480cf23e was 3517:1a3218a3fc5b, checked in by phi, 14 years ago

updated license headers, FSF moved office

File size: 9.9 KB
Line 
1//mainloop.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010
5 *  The code (may) include prototypes and comments (and maybe
6 *  other code fragements) from libpulse*. They are mostly copyrighted by:
7 *  Lennart Poettering <poettering@users.sourceforge.net> and
8 *  Pierre Ossman <drzeus@drzeus.cx>
9 *
10 *  This file is part of libroarpulse a part of RoarAudio,
11 *  a cross-platform sound system for both, home and professional use.
12 *  See README for details.
13 *
14 *  This file is free software; you can redistribute it and/or modify
15 *  it under the terms of the GNU General Public License version 3
16 *  as published by the Free Software Foundation.
17 *
18 *  RoarAudio is distributed in the hope that it will be useful,
19 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 *  GNU General Public License for more details.
22 *
23 *  You should have received a copy of the GNU General Public License
24 *  along with this software; see the file COPYING.  If not, write to
25 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
26 *  Boston, MA 02110-1301, USA.
27 *
28 *  NOTE for everyone want's to change something and send patches:
29 *  read README and HACKING! There a addition information on
30 *  the license of this document you need to read before you send
31 *  any patches.
32 *
33 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
34 *  or libpulse*:
35 *  The libs libroaresd, libroararts and libroarpulse link this libroar
36 *  and are therefore GPL. Because of this it may be illigal to use
37 *  them with any software that uses libesd, libartsc or libpulse*.
38 */
39
40#include <libroarpulse/libroarpulse.h>
41#ifdef ROAR_HAVE_H_POLL
42#include <poll.h>
43#endif
44
45#define MAX_IO_EVENTS    8
46
47#ifndef ROAR_HAVE_H_POLL
48struct pollfd {
49 int fd;
50 short events, revents;
51};
52
53typedef int nfds_t;
54
55#define POLLIN          0x001           /* There is data to read.  */
56#define POLLPRI         0x002           /* There is urgent data to read.  */
57#define POLLOUT         0x004           /* Writing now will not block.  */
58
59#define POLLERR         0x008           /* Error condition.  */
60#define POLLHUP         0x010           /* Hung up.  */
61#endif
62
63struct pa_io_event {
64 int used;
65 pa_mainloop_api *api;
66 int fd;
67 pa_io_event_flags_t events;
68 pa_io_event_cb_t cb;
69 void *userdata;
70 pa_io_event_destroy_cb_t destroy;
71};
72
73struct pa_mainloop {
74 pa_mainloop_api api;
75 pa_poll_func    poll_func;
76 void          * poll_userdata;
77 int             poll_timeout;
78 int             quit;
79 int             quitval;
80 pa_io_event     io_event[MAX_IO_EVENTS];
81 struct pollfd   pollfd[MAX_IO_EVENTS];
82 nfds_t          pollfds;
83};
84
85// IO EVENTS:
86static void _roar_pa_io_enable(pa_io_event* e, pa_io_event_flags_t events);
87
88/** Create a new IO event source object */
89static pa_io_event* _roar_pa_io_new(pa_mainloop_api*a, int fd,
90                                    pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata) {
91 pa_mainloop * mainloop = a->userdata;
92 pa_io_event * ret = NULL;
93 int i;
94
95 for (i = 0; i < MAX_IO_EVENTS; i++) {
96  if ( mainloop->io_event[i].used == 0 ) {
97   ret = &(mainloop->io_event[i]);
98  }
99 }
100
101 if ( ret == NULL )
102  return NULL;
103
104 ret->used = 1;
105
106 ret->api      = a;
107 ret->fd       = fd;
108 ret->cb       = cb;
109 ret->userdata = userdata;
110
111 // Callbacks:
112 ret->destroy  = NULL;
113
114 _roar_pa_io_enable(ret, events);
115
116 return ret;
117}
118/** Enable or disable IO events on this object */
119static void _roar_pa_io_enable(pa_io_event* e, pa_io_event_flags_t events) {
120 if ( e == NULL )
121  return;
122
123 e->events = events;
124}
125
126/** Free a IO event source object */
127static void _roar_pa_io_free(pa_io_event* e) {
128 if ( e == NULL )
129  return;
130
131 if ( e->destroy != NULL )
132  e->destroy(e->api, e, e->userdata);
133
134 e->used = 0;
135}
136/** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */
137static void _roar_pa_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb) {
138 if ( e == NULL )
139  return;
140
141 e->destroy = cb;
142}
143
144
145
146// API:
147
148void _roar_pa_mainloop_quit(pa_mainloop_api*a, int retval) {
149 pa_mainloop * m = a->userdata;
150 pa_mainloop_quit(m, retval);
151}
152
153/** Allocate a new main loop object */
154pa_mainloop *pa_mainloop_new(void) {
155 pa_mainloop * m = roar_mm_malloc(sizeof(pa_mainloop));
156
157 ROAR_DBG("pa_mainloop_new() = ?");
158
159 if ( m == NULL )
160  return NULL;
161
162 memset(m, 0, sizeof(pa_mainloop));
163
164 m->api.userdata       = m;
165
166 m->api.quit           = _roar_pa_mainloop_quit;
167
168 m->api.io_new         = _roar_pa_io_new;
169 m->api.io_enable      = _roar_pa_io_enable;
170 m->api.io_free        = _roar_pa_io_free;
171 m->api.io_set_destroy = _roar_pa_io_set_destroy;
172
173 ROAR_DBG("pa_mainloop_new() = %p", m);
174 return m;
175}
176
177/** Free a main loop object */
178void pa_mainloop_free(pa_mainloop* m) {
179 ROAR_DBG("pa_mainloop_free(m=%p) = ?", m);
180
181 if ( m == NULL )
182  return;
183
184 roar_mm_free(m);
185
186 ROAR_DBG("pa_mainloop_free(m=%p) = (void)", m);
187}
188
189/** Prepare for a single iteration of the main loop. Returns a negative value
190on error or exit request. timeout specifies a maximum timeout for the subsequent
191poll, or -1 for blocking behaviour. .*/
192int pa_mainloop_prepare(pa_mainloop *m, int timeout) {
193 short events;
194 int i;
195
196 ROAR_DBG("pa_mainloop_prepare(m=%p, timeout=%i) = ?", m, timeout);
197
198 if ( m == NULL )
199  return -1;
200
201 if ( m->quit )
202  return -2;
203
204 m->pollfds = 0;
205
206 for (i = 0; i < MAX_IO_EVENTS; i++) {
207  if ( m->io_event[i].used ) {
208   events = 0;
209
210   if ( m->io_event[i].events & PA_IO_EVENT_INPUT )
211    events |= POLLIN;
212
213   if ( m->io_event[i].events & PA_IO_EVENT_OUTPUT )
214    events |= POLLOUT;
215
216   if ( m->io_event[i].events & PA_IO_EVENT_HANGUP )
217    events |= POLLHUP;
218
219   if ( m->io_event[i].events & PA_IO_EVENT_ERROR )
220    events |= POLLERR;
221
222   if ( events == 0 )
223    continue;
224
225   m->pollfd[m->pollfds].fd     = m->io_event[i].fd;
226   m->pollfd[m->pollfds].events = events;
227   m->pollfds++;
228  }
229 }
230
231 m->poll_timeout = timeout;
232
233 ROAR_DBG("pa_mainloop_prepare(m=%p, timeout=%i) = 0", m, timeout);
234 return 0;
235}
236
237/** Execute the previously prepared poll. Returns a negative value on error.*/
238int pa_mainloop_poll(pa_mainloop *m) {
239 int ret;
240 int alive = 1;
241
242 ROAR_DBG("pa_mainloop_poll(m=%p) = ?", m);
243
244 if ( m == NULL )
245  return -1;
246
247 if ( m->quit )
248  return -2;
249
250 while (alive) {
251  if ( m->poll_func != NULL ) {
252   ret = m->poll_func(m->pollfd, m->pollfds, m->poll_timeout, m->poll_userdata);
253  } else {
254#ifdef ROAR_HAVE_H_POLL
255   ret = poll(m->pollfd, m->pollfds, m->poll_timeout);
256#else
257   ret = -1;
258#endif
259  }
260
261  if ( ret != -1 || ( errno != EAGAIN && errno != EINTR ) ) {
262   alive = 0;
263  }
264 }
265
266 ROAR_DBG("pa_mainloop_poll(m=%p) = %i", m, ret);
267 return ret;
268}
269
270/** Dispatch timeout, io and deferred events from the previously executed poll. Returns
271a negative value on error. On success returns the number of source dispatched. */
272int pa_mainloop_dispatch(pa_mainloop *m) {
273 pa_io_event_flags_t events;
274 int count = 0;
275 int i, h;
276
277 ROAR_DBG("pa_mainloop_dispatch(m=%p) = ?", m);
278
279 if ( m == NULL )
280  return -1;
281
282 if ( m->quit )
283  return -2;
284
285 for (i = 0; i < m->pollfds; i++) {
286  if ( m->pollfd[i].revents != 0 ) {
287   for (h = 0; h < MAX_IO_EVENTS; h++) {
288    if ( m->io_event[h].fd == m->pollfd[i].fd ) {
289     events = PA_IO_EVENT_NULL;
290
291     if ( m->pollfd[i].revents & POLLIN )
292      events |= PA_IO_EVENT_INPUT;
293
294     if ( m->pollfd[i].revents & POLLOUT )
295      events |= PA_IO_EVENT_OUTPUT;
296
297     if ( m->pollfd[i].revents & POLLHUP )
298      events |= PA_IO_EVENT_HANGUP;
299
300     if ( m->pollfd[i].revents & POLLERR )
301      events |= PA_IO_EVENT_ERROR;
302
303     if ( m->io_event[h].cb != NULL )
304      m->io_event[h].cb(&(m->api), &(m->io_event[h]), m->pollfd[i].fd, events, m->io_event[h].userdata);
305
306     count++;
307    }
308   }
309  }
310 }
311
312 ROAR_DBG("pa_mainloop_dispatch(m=%p) = %i", m, count);
313 return count;
314}
315
316/** Return the return value as specified with the main loop's quit() routine. */
317int pa_mainloop_get_retval(pa_mainloop *m) {
318 if ( m == NULL )
319  return -1;
320
321 return m->quitval;
322}
323
324/** Run a single iteration of the main loop. This is a convenience function
325for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
326Returns a negative value on error or exit request. If block is nonzero,
327block for events if none are queued. Optionally return the return value as
328specified with the main loop's quit() routine in the integer variable retval points
329to. On success returns the number of sources dispatched in this iteration. */
330int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
331 int r;
332
333 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = ?", m, block, retval);
334
335 if ( m == NULL )
336  return -1;
337
338 r = pa_mainloop_prepare(m, block ? -1 : 0);
339
340 if ( r >= 0 )
341  r = pa_mainloop_poll(m);
342
343 if ( r > 0 )
344  r = pa_mainloop_dispatch(m);
345
346 if ( r == -2 && retval != NULL ) {
347  *retval = m->quitval;
348 }
349
350 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = %i", m, block, retval, r);
351 return r;
352}
353
354/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
355int pa_mainloop_run(pa_mainloop *m, int *retval) {
356 int r = 1;
357
358 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p) = ?", m, retval);
359
360 if ( m == NULL )
361  return -1;
362
363 while (!(m->quit) && r > 0) {
364  r = pa_mainloop_iterate(m, 1, retval);
365 }
366
367 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p): r=%i", m, retval, r);
368
369 if ( r == -2 )
370  return 1;
371
372 if ( r < 0 )
373  return -1;
374
375 return 0;
376}
377
378/** Return the abstract main loop abstraction layer vtable for this main loop. */
379pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
380 if ( m == NULL )
381  return NULL;
382
383 return &(m->api);
384}
385
386/** Shutdown the main loop */
387void pa_mainloop_quit(pa_mainloop *m, int r) {
388 if ( m == NULL )
389  return;
390
391 m->quitval = r;
392 m->quit    = 1;
393}
394
395/** Interrupt a running poll (for threaded systems) */
396void pa_mainloop_wakeup(pa_mainloop *m);
397
398/** Change the poll() implementation */
399void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
400 if ( m == NULL )
401  return;
402
403 m->poll_func     = poll_func;
404 m->poll_userdata = userdata;
405}
406
407//ll
Note: See TracBrowser for help on using the repository browser.