2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Shams E. King
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include <pulse/rtclock.h>
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
33 #include <pulsecore/core-rtclock.h>
34 #include <pulsecore/core-util.h>
35 #include <pulsecore/log.h>
37 #include "dbus-util.h"
39 struct pa_dbus_wrap_connection
{
40 pa_mainloop_api
*mainloop
;
41 DBusConnection
*connection
;
42 pa_defer_event
* dispatch_event
;
43 pa_bool_t use_rtclock
:1;
47 pa_dbus_wrap_connection
*c
;
51 static void dispatch_cb(pa_mainloop_api
*ea
, pa_defer_event
*ev
, void *userdata
) {
52 DBusConnection
*conn
= userdata
;
54 if (dbus_connection_dispatch(conn
) == DBUS_DISPATCH_COMPLETE
) {
55 /* no more data to process, disable the deferred */
56 ea
->defer_enable(ev
, 0);
60 /* DBusDispatchStatusFunction callback for the pa mainloop */
61 static void dispatch_status(DBusConnection
*conn
, DBusDispatchStatus status
, void *userdata
) {
62 pa_dbus_wrap_connection
*c
= userdata
;
68 case DBUS_DISPATCH_COMPLETE
:
69 c
->mainloop
->defer_enable(c
->dispatch_event
, 0);
72 case DBUS_DISPATCH_DATA_REMAINS
:
73 case DBUS_DISPATCH_NEED_MEMORY
:
75 c
->mainloop
->defer_enable(c
->dispatch_event
, 1);
80 static pa_io_event_flags_t
get_watch_flags(DBusWatch
*watch
) {
82 pa_io_event_flags_t events
= 0;
86 flags
= dbus_watch_get_flags(watch
);
88 /* no watch flags for disabled watches */
89 if (!dbus_watch_get_enabled(watch
))
90 return PA_IO_EVENT_NULL
;
92 if (flags
& DBUS_WATCH_READABLE
)
93 events
|= PA_IO_EVENT_INPUT
;
94 if (flags
& DBUS_WATCH_WRITABLE
)
95 events
|= PA_IO_EVENT_OUTPUT
;
97 return events
| PA_IO_EVENT_HANGUP
| PA_IO_EVENT_ERROR
;
100 /* pa_io_event_cb_t IO event handler */
101 static void handle_io_event(pa_mainloop_api
*ea
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
102 unsigned int flags
= 0;
103 DBusWatch
*watch
= userdata
;
105 #if HAVE_DBUS_WATCH_GET_UNIX_FD
106 pa_assert(fd
== dbus_watch_get_unix_fd(watch
));
108 pa_assert(fd
== dbus_watch_get_fd(watch
));
111 if (!dbus_watch_get_enabled(watch
)) {
112 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch
, fd
);
116 if (events
& PA_IO_EVENT_INPUT
)
117 flags
|= DBUS_WATCH_READABLE
;
118 if (events
& PA_IO_EVENT_OUTPUT
)
119 flags
|= DBUS_WATCH_WRITABLE
;
120 if (events
& PA_IO_EVENT_HANGUP
)
121 flags
|= DBUS_WATCH_HANGUP
;
122 if (events
& PA_IO_EVENT_ERROR
)
123 flags
|= DBUS_WATCH_ERROR
;
125 dbus_watch_handle(watch
, flags
);
128 /* pa_time_event_cb_t timer event handler */
129 static void handle_time_event(pa_mainloop_api
*ea
, pa_time_event
* e
, const struct timeval
*t
, void *userdata
) {
131 struct timeout_data
*d
= userdata
;
136 if (dbus_timeout_get_enabled(d
->timeout
)) {
137 dbus_timeout_handle(d
->timeout
);
139 /* restart it for the next scheduled time */
140 ea
->time_restart(e
, pa_timeval_rtstore(&tv
, pa_timeval_load(t
) + dbus_timeout_get_interval(d
->timeout
) * PA_USEC_PER_MSEC
, d
->c
->use_rtclock
));
144 /* DBusAddWatchFunction callback for pa mainloop */
145 static dbus_bool_t
add_watch(DBusWatch
*watch
, void *data
) {
146 pa_dbus_wrap_connection
*c
= data
;
152 ev
= c
->mainloop
->io_new(
154 #if HAVE_DBUS_WATCH_GET_UNIX_FD
155 dbus_watch_get_unix_fd(watch
),
157 dbus_watch_get_fd(watch
),
159 get_watch_flags(watch
), handle_io_event
, watch
);
161 dbus_watch_set_data(watch
, ev
, NULL
);
166 /* DBusRemoveWatchFunction callback for pa mainloop */
167 static void remove_watch(DBusWatch
*watch
, void *data
) {
168 pa_dbus_wrap_connection
*c
= data
;
174 if ((ev
= dbus_watch_get_data(watch
)))
175 c
->mainloop
->io_free(ev
);
178 /* DBusWatchToggledFunction callback for pa mainloop */
179 static void toggle_watch(DBusWatch
*watch
, void *data
) {
180 pa_dbus_wrap_connection
*c
= data
;
186 pa_assert_se(ev
= dbus_watch_get_data(watch
));
188 /* get_watch_flags() checks if the watch is enabled */
189 c
->mainloop
->io_enable(ev
, get_watch_flags(watch
));
192 static void time_event_destroy_cb(pa_mainloop_api
*a
, pa_time_event
*e
, void *userdata
) {
196 /* DBusAddTimeoutFunction callback for pa mainloop */
197 static dbus_bool_t
add_timeout(DBusTimeout
*timeout
, void *data
) {
198 pa_dbus_wrap_connection
*c
= data
;
201 struct timeout_data
*d
;
206 if (!dbus_timeout_get_enabled(timeout
))
209 d
= pa_xnew(struct timeout_data
, 1);
211 d
->timeout
= timeout
;
212 ev
= c
->mainloop
->time_new(c
->mainloop
, pa_timeval_rtstore(&tv
, pa_rtclock_now() + dbus_timeout_get_interval(timeout
) * PA_USEC_PER_MSEC
, c
->use_rtclock
), handle_time_event
, d
);
213 c
->mainloop
->time_set_destroy(ev
, time_event_destroy_cb
);
215 dbus_timeout_set_data(timeout
, ev
, NULL
);
220 /* DBusRemoveTimeoutFunction callback for pa mainloop */
221 static void remove_timeout(DBusTimeout
*timeout
, void *data
) {
222 pa_dbus_wrap_connection
*c
= data
;
228 if ((ev
= dbus_timeout_get_data(timeout
)))
229 c
->mainloop
->time_free(ev
);
232 /* DBusTimeoutToggledFunction callback for pa mainloop */
233 static void toggle_timeout(DBusTimeout
*timeout
, void *data
) {
234 struct timeout_data
*d
= data
;
242 pa_assert_se(ev
= dbus_timeout_get_data(timeout
));
244 if (dbus_timeout_get_enabled(timeout
)) {
245 d
->c
->mainloop
->time_restart(ev
, pa_timeval_rtstore(&tv
, pa_rtclock_now() + dbus_timeout_get_interval(timeout
) * PA_USEC_PER_MSEC
, d
->c
->use_rtclock
));
247 d
->c
->mainloop
->time_restart(ev
, pa_timeval_rtstore(&tv
, PA_USEC_INVALID
, d
->c
->use_rtclock
));
250 static void wakeup_main(void *userdata
) {
251 pa_dbus_wrap_connection
*c
= userdata
;
255 /* this will wakeup the mainloop and dispatch events, although
256 * it may not be the cleanest way of accomplishing it */
257 c
->mainloop
->defer_enable(c
->dispatch_event
, 1);
260 pa_dbus_wrap_connection
* pa_dbus_wrap_connection_new(pa_mainloop_api
*m
, pa_bool_t use_rtclock
, DBusBusType type
, DBusError
*error
) {
261 DBusConnection
*conn
;
262 pa_dbus_wrap_connection
*pconn
;
265 pa_assert(type
== DBUS_BUS_SYSTEM
|| type
== DBUS_BUS_SESSION
|| type
== DBUS_BUS_STARTER
);
267 if (!(conn
= dbus_bus_get_private(type
, error
)))
270 pconn
= pa_xnew(pa_dbus_wrap_connection
, 1);
272 pconn
->connection
= conn
;
273 pconn
->use_rtclock
= use_rtclock
;
275 dbus_connection_set_exit_on_disconnect(conn
, FALSE
);
276 dbus_connection_set_dispatch_status_function(conn
, dispatch_status
, pconn
, NULL
);
277 dbus_connection_set_watch_functions(conn
, add_watch
, remove_watch
, toggle_watch
, pconn
, NULL
);
278 dbus_connection_set_timeout_functions(conn
, add_timeout
, remove_timeout
, toggle_timeout
, pconn
, NULL
);
279 dbus_connection_set_wakeup_main_function(conn
, wakeup_main
, pconn
, NULL
);
281 pconn
->dispatch_event
= pconn
->mainloop
->defer_new(pconn
->mainloop
, dispatch_cb
, conn
);
283 pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
284 type
== DBUS_BUS_SYSTEM
? "system" : (type
== DBUS_BUS_SESSION
? "session" : "starter"),
285 pa_strnull((id
= dbus_connection_get_server_id(conn
))),
286 pa_strnull(dbus_bus_get_unique_name(conn
)));
293 pa_dbus_wrap_connection
* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api
*m
, pa_bool_t use_rtclock
, DBusConnection
*conn
) {
294 pa_dbus_wrap_connection
*pconn
;
299 pconn
= pa_xnew(pa_dbus_wrap_connection
, 1);
301 pconn
->connection
= dbus_connection_ref(conn
);
302 pconn
->use_rtclock
= use_rtclock
;
304 dbus_connection_set_exit_on_disconnect(conn
, FALSE
);
305 dbus_connection_set_dispatch_status_function(conn
, dispatch_status
, pconn
, NULL
);
306 dbus_connection_set_watch_functions(conn
, add_watch
, remove_watch
, toggle_watch
, pconn
, NULL
);
307 dbus_connection_set_timeout_functions(conn
, add_timeout
, remove_timeout
, toggle_timeout
, pconn
, NULL
);
308 dbus_connection_set_wakeup_main_function(conn
, wakeup_main
, pconn
, NULL
);
310 pconn
->dispatch_event
= pconn
->mainloop
->defer_new(pconn
->mainloop
, dispatch_cb
, conn
);
315 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection
* c
) {
318 if (dbus_connection_get_is_connected(c
->connection
)) {
319 dbus_connection_close(c
->connection
);
320 /* must process remaining messages, bit of a kludge to handle
321 * both unload and shutdown */
322 while (dbus_connection_read_write_dispatch(c
->connection
, -1))
326 c
->mainloop
->defer_free(c
->dispatch_event
);
327 dbus_connection_unref(c
->connection
);
331 DBusConnection
* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection
*c
) {
333 pa_assert(c
->connection
);
335 return c
->connection
;
338 int pa_dbus_add_matches(DBusConnection
*c
, DBusError
*error
, ...) {
347 while ((t
= va_arg(ap
, const char*))) {
348 dbus_bus_add_match(c
, t
, error
);
350 if (dbus_error_is_set(error
))
365 pa_assert_se(t
= va_arg(ap
, const char*));
368 dbus_bus_remove_match(c
, t
, &e
);
376 void pa_dbus_remove_matches(DBusConnection
*c
, ...) {
383 dbus_error_init(&error
);
386 while ((t
= va_arg(ap
, const char*))) {
387 dbus_bus_remove_match(c
, t
, &error
);
388 dbus_error_free(&error
);
393 pa_dbus_pending
*pa_dbus_pending_new(
396 DBusPendingCall
*pending
,
404 p
= pa_xnew(pa_dbus_pending
, 1);
407 p
->pending
= pending
;
408 p
->context_data
= context_data
;
409 p
->call_data
= call_data
;
411 PA_LLIST_INIT(pa_dbus_pending
, p
);
416 void pa_dbus_pending_free(pa_dbus_pending
*p
) {
420 dbus_pending_call_cancel(p
->pending
);
421 dbus_pending_call_unref(p
->pending
);
425 dbus_message_unref(p
->message
);
430 void pa_dbus_sync_pending_list(pa_dbus_pending
**p
) {
433 while (*p
&& dbus_connection_read_write_dispatch((*p
)->connection
, -1))
437 void pa_dbus_free_pending_list(pa_dbus_pending
**p
) {
443 PA_LLIST_REMOVE(pa_dbus_pending
, *p
, i
);
444 pa_dbus_pending_free(i
);