source: roaraudio/libroarpulse/mainloop.c @ 5507:625d7c213f05

Last change on this file since 5507:625d7c213f05 was 5381:430b1d26e12d, checked in by phi, 12 years ago

updated copyright years

File size: 9.9 KB
Line 
1//mainloop.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2012
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 h;
276 nfds_t i;
277
278 ROAR_DBG("pa_mainloop_dispatch(m=%p) = ?", m);
279
280 if ( m == NULL )
281  return -1;
282
283 if ( m->quit )
284  return -2;
285
286 for (i = 0; i < m->pollfds; i++) {
287  if ( m->pollfd[i].revents != 0 ) {
288   for (h = 0; h < MAX_IO_EVENTS; h++) {
289    if ( m->io_event[h].fd == m->pollfd[i].fd ) {
290     events = PA_IO_EVENT_NULL;
291
292     if ( m->pollfd[i].revents & POLLIN )
293      events |= PA_IO_EVENT_INPUT;
294
295     if ( m->pollfd[i].revents & POLLOUT )
296      events |= PA_IO_EVENT_OUTPUT;
297
298     if ( m->pollfd[i].revents & POLLHUP )
299      events |= PA_IO_EVENT_HANGUP;
300
301     if ( m->pollfd[i].revents & POLLERR )
302      events |= PA_IO_EVENT_ERROR;
303
304     if ( m->io_event[h].cb != NULL )
305      m->io_event[h].cb(&(m->api), &(m->io_event[h]), m->pollfd[i].fd, events, m->io_event[h].userdata);
306
307     count++;
308    }
309   }
310  }
311 }
312
313 ROAR_DBG("pa_mainloop_dispatch(m=%p) = %i", m, count);
314 return count;
315}
316
317/** Return the return value as specified with the main loop's quit() routine. */
318int pa_mainloop_get_retval(pa_mainloop *m) {
319 if ( m == NULL )
320  return -1;
321
322 return m->quitval;
323}
324
325/** Run a single iteration of the main loop. This is a convenience function
326for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
327Returns a negative value on error or exit request. If block is nonzero,
328block for events if none are queued. Optionally return the return value as
329specified with the main loop's quit() routine in the integer variable retval points
330to. On success returns the number of sources dispatched in this iteration. */
331int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) {
332 int r;
333
334 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = ?", m, block, retval);
335
336 if ( m == NULL )
337  return -1;
338
339 r = pa_mainloop_prepare(m, block ? -1 : 0);
340
341 if ( r >= 0 )
342  r = pa_mainloop_poll(m);
343
344 if ( r > 0 )
345  r = pa_mainloop_dispatch(m);
346
347 if ( r == -2 && retval != NULL ) {
348  *retval = m->quitval;
349 }
350
351 ROAR_DBG("pa_mainloop_iterate(m=%p, block=%i, retval=%p) = %i", m, block, retval, r);
352 return r;
353}
354
355/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
356int pa_mainloop_run(pa_mainloop *m, int *retval) {
357 int r = 1;
358
359 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p) = ?", m, retval);
360
361 if ( m == NULL )
362  return -1;
363
364 while (!(m->quit) && r > 0) {
365  r = pa_mainloop_iterate(m, 1, retval);
366 }
367
368 ROAR_DBG("pa_mainloop_run(m=%p, retval=%p): r=%i", m, retval, r);
369
370 if ( r == -2 )
371  return 1;
372
373 if ( r < 0 )
374  return -1;
375
376 return 0;
377}
378
379/** Return the abstract main loop abstraction layer vtable for this main loop. */
380pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) {
381 if ( m == NULL )
382  return NULL;
383
384 return &(m->api);
385}
386
387/** Shutdown the main loop */
388void pa_mainloop_quit(pa_mainloop *m, int r) {
389 if ( m == NULL )
390  return;
391
392 m->quitval = r;
393 m->quit    = 1;
394}
395
396/** Interrupt a running poll (for threaded systems) */
397void pa_mainloop_wakeup(pa_mainloop *m);
398
399/** Change the poll() implementation */
400void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata) {
401 if ( m == NULL )
402  return;
403
404 m->poll_func     = poll_func;
405 m->poll_userdata = userdata;
406}
407
408//ll
Note: See TracBrowser for help on using the repository browser.