source: roaraudio/libroarpulse/mainloop.c @ 3492:e9d7e26291c3

Last change on this file since 3492:e9d7e26291c3 was 3492:e9d7e26291c3, checked in by phi, 14 years ago

define nfds_t

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