2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
6 PulseAudio 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.1 of the License,
9 or (at your option) any later version.
11 PulseAudio 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.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <pulse/xmalloc.h>
27 #include <pulse/timeval.h>
29 #include <pulsecore/core-util.h>
30 #include <pulsecore/log.h>
31 #include <pulsecore/llist.h>
34 #include "glib-mainloop.h"
37 pa_glib_mainloop
*mainloop
;
43 pa_io_event_cb_t callback
;
45 pa_io_event_destroy_cb_t destroy_callback
;
47 PA_LLIST_FIELDS(pa_io_event
);
50 struct pa_time_event
{
51 pa_glib_mainloop
*mainloop
;
55 struct timeval timeval
;
57 pa_time_event_cb_t callback
;
59 pa_time_event_destroy_cb_t destroy_callback
;
61 PA_LLIST_FIELDS(pa_time_event
);
64 struct pa_defer_event
{
65 pa_glib_mainloop
*mainloop
;
70 pa_defer_event_cb_t callback
;
72 pa_defer_event_destroy_cb_t destroy_callback
;
74 PA_LLIST_FIELDS(pa_defer_event
);
77 struct pa_glib_mainloop
{
81 GMainContext
*context
;
83 PA_LLIST_HEAD(pa_io_event
, io_events
);
84 PA_LLIST_HEAD(pa_time_event
, time_events
);
85 PA_LLIST_HEAD(pa_defer_event
, defer_events
);
87 int n_enabled_defer_events
, n_enabled_time_events
;
88 int io_events_please_scan
, time_events_please_scan
, defer_events_please_scan
;
90 pa_time_event
*cached_next_time_event
;
93 static void cleanup_io_events(pa_glib_mainloop
*g
, int force
) {
98 pa_io_event
*n
= e
->next
;
100 if (!force
&& g
->io_events_please_scan
<= 0)
103 if (force
|| e
->dead
) {
104 PA_LLIST_REMOVE(pa_io_event
, g
->io_events
, e
);
107 g_assert(g
->io_events_please_scan
> 0);
108 g
->io_events_please_scan
--;
111 if (e
->poll_fd_added
)
112 g_source_remove_poll(&g
->source
, &e
->poll_fd
);
114 if (e
->destroy_callback
)
115 e
->destroy_callback(&g
->api
, e
, e
->userdata
);
123 g_assert(g
->io_events_please_scan
== 0);
126 static void cleanup_time_events(pa_glib_mainloop
*g
, int force
) {
131 pa_time_event
*n
= e
->next
;
133 if (!force
&& g
->time_events_please_scan
<= 0)
136 if (force
|| e
->dead
) {
137 PA_LLIST_REMOVE(pa_time_event
, g
->time_events
, e
);
140 g_assert(g
->time_events_please_scan
> 0);
141 g
->time_events_please_scan
--;
144 if (!e
->dead
&& e
->enabled
) {
145 g_assert(g
->n_enabled_time_events
> 0);
146 g
->n_enabled_time_events
--;
149 if (e
->destroy_callback
)
150 e
->destroy_callback(&g
->api
, e
, e
->userdata
);
158 g_assert(g
->time_events_please_scan
== 0);
161 static void cleanup_defer_events(pa_glib_mainloop
*g
, int force
) {
166 pa_defer_event
*n
= e
->next
;
168 if (!force
&& g
->defer_events_please_scan
<= 0)
171 if (force
|| e
->dead
) {
172 PA_LLIST_REMOVE(pa_defer_event
, g
->defer_events
, e
);
175 g_assert(g
->defer_events_please_scan
> 0);
176 g
->defer_events_please_scan
--;
179 if (!e
->dead
&& e
->enabled
) {
180 g_assert(g
->n_enabled_defer_events
> 0);
181 g
->n_enabled_defer_events
--;
184 if (e
->destroy_callback
)
185 e
->destroy_callback(&g
->api
, e
, e
->userdata
);
193 g_assert(g
->defer_events_please_scan
== 0);
196 static gushort
map_flags_to_glib(pa_io_event_flags_t flags
) {
198 ((flags
& PA_IO_EVENT_INPUT
? G_IO_IN
: 0) |
199 (flags
& PA_IO_EVENT_OUTPUT
? G_IO_OUT
: 0) |
200 (flags
& PA_IO_EVENT_ERROR
? G_IO_ERR
: 0) |
201 (flags
& PA_IO_EVENT_HANGUP
? G_IO_HUP
: 0));
204 static pa_io_event_flags_t
map_flags_from_glib(gushort flags
) {
206 (flags
& G_IO_IN
? PA_IO_EVENT_INPUT
: 0) |
207 (flags
& G_IO_OUT
? PA_IO_EVENT_OUTPUT
: 0) |
208 (flags
& G_IO_ERR
? PA_IO_EVENT_ERROR
: 0) |
209 (flags
& G_IO_HUP
? PA_IO_EVENT_HANGUP
: 0);
212 static pa_io_event
* glib_io_new(
215 pa_io_event_flags_t f
,
223 g_assert(m
->userdata
);
229 e
= pa_xnew(pa_io_event
, 1);
234 e
->poll_fd
.events
= map_flags_to_glib(f
);
235 e
->poll_fd
.revents
= 0;
238 e
->userdata
= userdata
;
239 e
->destroy_callback
= NULL
;
241 PA_LLIST_PREPEND(pa_io_event
, g
->io_events
, e
);
243 g_source_add_poll(&g
->source
, &e
->poll_fd
);
244 e
->poll_fd_added
= 1;
249 static void glib_io_enable(pa_io_event
*e
, pa_io_event_flags_t f
) {
253 e
->poll_fd
.events
= map_flags_to_glib(f
);
256 static void glib_io_free(pa_io_event
*e
) {
261 e
->mainloop
->io_events_please_scan
++;
263 if (e
->poll_fd_added
) {
264 g_source_remove_poll(&e
->mainloop
->source
, &e
->poll_fd
);
265 e
->poll_fd_added
= 0;
269 static void glib_io_set_destroy(pa_io_event
*e
, pa_io_event_destroy_cb_t cb
) {
273 e
->destroy_callback
= cb
;
278 static pa_time_event
* glib_time_new(
280 const struct timeval
*tv
,
281 pa_time_event_cb_t cb
,
288 g_assert(m
->userdata
);
293 e
= pa_xnew(pa_time_event
, 1);
297 if ((e
->enabled
= !!tv
)) {
299 g
->n_enabled_time_events
++;
301 if (g
->cached_next_time_event
) {
302 g_assert(g
->cached_next_time_event
->enabled
);
304 if (pa_timeval_cmp(tv
, &g
->cached_next_time_event
->timeval
) < 0)
305 g
->cached_next_time_event
= e
;
310 e
->userdata
= userdata
;
311 e
->destroy_callback
= NULL
;
313 PA_LLIST_PREPEND(pa_time_event
, g
->time_events
, e
);
318 static void glib_time_restart(pa_time_event
*e
, const struct timeval
*tv
) {
322 if (e
->enabled
&& !tv
) {
323 g_assert(e
->mainloop
->n_enabled_time_events
> 0);
324 e
->mainloop
->n_enabled_time_events
--;
325 } else if (!e
->enabled
&& tv
)
326 e
->mainloop
->n_enabled_time_events
++;
328 if ((e
->enabled
= !!tv
))
331 if (e
->mainloop
->cached_next_time_event
&& e
->enabled
) {
332 g_assert(e
->mainloop
->cached_next_time_event
->enabled
);
334 if (pa_timeval_cmp(tv
, &e
->mainloop
->cached_next_time_event
->timeval
) < 0)
335 e
->mainloop
->cached_next_time_event
= e
;
336 } else if (e
->mainloop
->cached_next_time_event
== e
)
337 e
->mainloop
->cached_next_time_event
= NULL
;
340 static void glib_time_free(pa_time_event
*e
) {
345 e
->mainloop
->time_events_please_scan
++;
348 e
->mainloop
->n_enabled_time_events
--;
350 if (e
->mainloop
->cached_next_time_event
== e
)
351 e
->mainloop
->cached_next_time_event
= NULL
;
354 static void glib_time_set_destroy(pa_time_event
*e
, pa_time_event_destroy_cb_t cb
) {
358 e
->destroy_callback
= cb
;
361 /* Deferred sources */
363 static pa_defer_event
* glib_defer_new(
365 pa_defer_event_cb_t cb
,
372 g_assert(m
->userdata
);
377 e
= pa_xnew(pa_defer_event
, 1);
382 g
->n_enabled_defer_events
++;
385 e
->userdata
= userdata
;
386 e
->destroy_callback
= NULL
;
388 PA_LLIST_PREPEND(pa_defer_event
, g
->defer_events
, e
);
392 static void glib_defer_enable(pa_defer_event
*e
, int b
) {
396 if (e
->enabled
&& !b
) {
397 g_assert(e
->mainloop
->n_enabled_defer_events
> 0);
398 e
->mainloop
->n_enabled_defer_events
--;
399 } else if (!e
->enabled
&& b
)
400 e
->mainloop
->n_enabled_defer_events
++;
405 static void glib_defer_free(pa_defer_event
*e
) {
410 e
->mainloop
->defer_events_please_scan
++;
413 g_assert(e
->mainloop
->n_enabled_defer_events
> 0);
414 e
->mainloop
->n_enabled_defer_events
--;
418 static void glib_defer_set_destroy(pa_defer_event
*e
, pa_defer_event_destroy_cb_t cb
) {
422 e
->destroy_callback
= cb
;
427 static void glib_quit(pa_mainloop_api
*a
, int retval
) {
429 g_warning("quit() ignored");
434 static pa_time_event
* find_next_time_event(pa_glib_mainloop
*g
) {
435 pa_time_event
*t
, *n
= NULL
;
438 if (g
->cached_next_time_event
)
439 return g
->cached_next_time_event
;
441 for (t
= g
->time_events
; t
; t
= t
->next
) {
443 if (t
->dead
|| !t
->enabled
)
446 if (!n
|| pa_timeval_cmp(&t
->timeval
, &n
->timeval
) < 0) {
449 /* Shortcut for tv = { 0, 0 } */
450 if (n
->timeval
.tv_sec
<= 0)
455 g
->cached_next_time_event
= n
;
459 static void scan_dead(pa_glib_mainloop
*g
) {
462 if (g
->io_events_please_scan
)
463 cleanup_io_events(g
, 0);
465 if (g
->time_events_please_scan
)
466 cleanup_time_events(g
, 0);
468 if (g
->defer_events_please_scan
)
469 cleanup_defer_events(g
, 0);
472 static gboolean
prepare_func(GSource
*source
, gint
*timeout
) {
473 pa_glib_mainloop
*g
= (pa_glib_mainloop
*) source
;
480 if (g
->n_enabled_defer_events
) {
483 } else if (g
->n_enabled_time_events
) {
486 struct timeval tvnow
;
489 t
= find_next_time_event(g
);
492 g_get_current_time(&now
);
493 tvnow
.tv_sec
= now
.tv_sec
;
494 tvnow
.tv_usec
= now
.tv_usec
;
496 if (pa_timeval_cmp(&t
->timeval
, &tvnow
) <= 0) {
500 usec
= pa_timeval_diff(&t
->timeval
, &tvnow
);
501 *timeout
= (gint
) (usec
/ 1000);
507 static gboolean
check_func(GSource
*source
) {
508 pa_glib_mainloop
*g
= (pa_glib_mainloop
*) source
;
513 if (g
->n_enabled_defer_events
)
515 else if (g
->n_enabled_time_events
) {
518 struct timeval tvnow
;
520 t
= find_next_time_event(g
);
523 g_get_current_time(&now
);
524 tvnow
.tv_sec
= now
.tv_sec
;
525 tvnow
.tv_usec
= now
.tv_usec
;
527 if (pa_timeval_cmp(&t
->timeval
, &tvnow
) <= 0)
531 for (e
= g
->io_events
; e
; e
= e
->next
)
532 if (!e
->dead
&& e
->poll_fd
.revents
!= 0)
538 static gboolean
dispatch_func(GSource
*source
, GSourceFunc callback
, gpointer userdata
) {
539 pa_glib_mainloop
*g
= (pa_glib_mainloop
*) source
;
544 if (g
->n_enabled_defer_events
) {
547 for (d
= g
->defer_events
; d
; d
= d
->next
) {
548 if (d
->dead
|| !d
->enabled
)
556 d
->callback(&g
->api
, d
, d
->userdata
);
560 if (g
->n_enabled_time_events
) {
562 struct timeval tvnow
;
565 t
= find_next_time_event(g
);
568 g_get_current_time(&now
);
569 tvnow
.tv_sec
= now
.tv_sec
;
570 tvnow
.tv_usec
= now
.tv_usec
;
572 if (pa_timeval_cmp(&t
->timeval
, &tvnow
) <= 0) {
574 /* Disable time event */
575 glib_time_restart(t
, NULL
);
577 t
->callback(&g
->api
, t
, &t
->timeval
, t
->userdata
);
582 for (e
= g
->io_events
; e
; e
= e
->next
)
583 if (!e
->dead
&& e
->poll_fd
.revents
!= 0) {
584 e
->callback(&g
->api
, e
, e
->poll_fd
.fd
, map_flags_from_glib(e
->poll_fd
.revents
), e
->userdata
);
585 e
->poll_fd
.revents
= 0;
592 static const pa_mainloop_api vtable
= {
595 .io_new
= glib_io_new
,
596 .io_enable
= glib_io_enable
,
597 .io_free
= glib_io_free
,
598 .io_set_destroy
= glib_io_set_destroy
,
600 .time_new
= glib_time_new
,
601 .time_restart
= glib_time_restart
,
602 .time_free
= glib_time_free
,
603 .time_set_destroy
= glib_time_set_destroy
,
605 .defer_new
= glib_defer_new
,
606 .defer_enable
= glib_defer_enable
,
607 .defer_free
= glib_defer_free
,
608 .defer_set_destroy
= glib_defer_set_destroy
,
613 pa_glib_mainloop
*pa_glib_mainloop_new(GMainContext
*c
) {
616 static GSourceFuncs source_funcs
= {
625 g
= (pa_glib_mainloop
*) g_source_new(&source_funcs
, sizeof(pa_glib_mainloop
));
626 g_main_context_ref(g
->context
= c
? c
: g_main_context_default());
631 PA_LLIST_HEAD_INIT(pa_io_event
, g
->io_events
);
632 PA_LLIST_HEAD_INIT(pa_time_event
, g
->time_events
);
633 PA_LLIST_HEAD_INIT(pa_defer_event
, g
->defer_events
);
635 g
->n_enabled_defer_events
= g
->n_enabled_time_events
= 0;
636 g
->io_events_please_scan
= g
->time_events_please_scan
= g
->defer_events_please_scan
= 0;
638 g
->cached_next_time_event
= NULL
;
640 g_source_attach(&g
->source
, g
->context
);
641 g_source_set_can_recurse(&g
->source
, FALSE
);
646 void pa_glib_mainloop_free(pa_glib_mainloop
* g
) {
649 cleanup_io_events(g
, 1);
650 cleanup_defer_events(g
, 1);
651 cleanup_time_events(g
, 1);
653 g_main_context_unref(g
->context
);
654 g_source_destroy(&g
->source
);
655 g_source_unref(&g
->source
);
658 pa_mainloop_api
* pa_glib_mainloop_get_api(pa_glib_mainloop
*g
) {