]> code.delx.au - pulseaudio/blob - src/pulsecore/dbus-util.c
Merge branch 'master' into dbus-work
[pulseaudio] / src / pulsecore / dbus-util.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Shams E. King
6
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.
11
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.
16
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
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdarg.h>
28
29 #include <pulse/rtclock.h>
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
32
33 #include <pulsecore/core-rtclock.h>
34 #include <pulsecore/core-util.h>
35 #include <pulsecore/log.h>
36
37 #include "dbus-util.h"
38
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;
44 };
45
46 struct timeout_data {
47 pa_dbus_wrap_connection *c;
48 DBusTimeout *timeout;
49 };
50
51 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
52 DBusConnection *conn = userdata;
53
54 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
55 /* no more data to process, disable the deferred */
56 ea->defer_enable(ev, 0);
57 }
58 }
59
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;
63
64 pa_assert(c);
65
66 switch(status) {
67
68 case DBUS_DISPATCH_COMPLETE:
69 c->mainloop->defer_enable(c->dispatch_event, 0);
70 break;
71
72 case DBUS_DISPATCH_DATA_REMAINS:
73 case DBUS_DISPATCH_NEED_MEMORY:
74 default:
75 c->mainloop->defer_enable(c->dispatch_event, 1);
76 break;
77 }
78 }
79
80 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
81 unsigned int flags;
82 pa_io_event_flags_t events = 0;
83
84 pa_assert(watch);
85
86 flags = dbus_watch_get_flags(watch);
87
88 /* no watch flags for disabled watches */
89 if (!dbus_watch_get_enabled(watch))
90 return PA_IO_EVENT_NULL;
91
92 if (flags & DBUS_WATCH_READABLE)
93 events |= PA_IO_EVENT_INPUT;
94 if (flags & DBUS_WATCH_WRITABLE)
95 events |= PA_IO_EVENT_OUTPUT;
96
97 return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
98 }
99
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;
104
105 #if HAVE_DBUS_WATCH_GET_UNIX_FD
106 pa_assert(fd == dbus_watch_get_unix_fd(watch));
107 #else
108 pa_assert(fd == dbus_watch_get_fd(watch));
109 #endif
110
111 if (!dbus_watch_get_enabled(watch)) {
112 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
113 return;
114 }
115
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;
124
125 dbus_watch_handle(watch, flags);
126 }
127
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) {
130 struct timeval tv;
131 struct timeout_data *d = userdata;
132
133 pa_assert(d);
134 pa_assert(d->c);
135
136 if (dbus_timeout_get_enabled(d->timeout)) {
137 dbus_timeout_handle(d->timeout);
138
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));
141 }
142 }
143
144 /* DBusAddWatchFunction callback for pa mainloop */
145 static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
146 pa_dbus_wrap_connection *c = data;
147 pa_io_event *ev;
148
149 pa_assert(watch);
150 pa_assert(c);
151
152 ev = c->mainloop->io_new(
153 c->mainloop,
154 #if HAVE_DBUS_WATCH_GET_UNIX_FD
155 dbus_watch_get_unix_fd(watch),
156 #else
157 dbus_watch_get_fd(watch),
158 #endif
159 get_watch_flags(watch), handle_io_event, watch);
160
161 dbus_watch_set_data(watch, ev, NULL);
162
163 return TRUE;
164 }
165
166 /* DBusRemoveWatchFunction callback for pa mainloop */
167 static void remove_watch(DBusWatch *watch, void *data) {
168 pa_dbus_wrap_connection *c = data;
169 pa_io_event *ev;
170
171 pa_assert(watch);
172 pa_assert(c);
173
174 if ((ev = dbus_watch_get_data(watch)))
175 c->mainloop->io_free(ev);
176 }
177
178 /* DBusWatchToggledFunction callback for pa mainloop */
179 static void toggle_watch(DBusWatch *watch, void *data) {
180 pa_dbus_wrap_connection *c = data;
181 pa_io_event *ev;
182
183 pa_assert(watch);
184 pa_assert(c);
185
186 pa_assert_se(ev = dbus_watch_get_data(watch));
187
188 /* get_watch_flags() checks if the watch is enabled */
189 c->mainloop->io_enable(ev, get_watch_flags(watch));
190 }
191
192 static void time_event_destroy_cb(pa_mainloop_api *a, pa_time_event *e, void *userdata) {
193 pa_xfree(userdata);
194 }
195
196 /* DBusAddTimeoutFunction callback for pa mainloop */
197 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
198 pa_dbus_wrap_connection *c = data;
199 pa_time_event *ev;
200 struct timeval tv;
201 struct timeout_data *d;
202
203 pa_assert(timeout);
204 pa_assert(c);
205
206 if (!dbus_timeout_get_enabled(timeout))
207 return FALSE;
208
209 d = pa_xnew(struct timeout_data, 1);
210 d->c = c;
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);
214
215 dbus_timeout_set_data(timeout, ev, NULL);
216
217 return TRUE;
218 }
219
220 /* DBusRemoveTimeoutFunction callback for pa mainloop */
221 static void remove_timeout(DBusTimeout *timeout, void *data) {
222 pa_dbus_wrap_connection *c = data;
223 pa_time_event *ev;
224
225 pa_assert(timeout);
226 pa_assert(c);
227
228 if ((ev = dbus_timeout_get_data(timeout)))
229 c->mainloop->time_free(ev);
230 }
231
232 /* DBusTimeoutToggledFunction callback for pa mainloop */
233 static void toggle_timeout(DBusTimeout *timeout, void *data) {
234 struct timeout_data *d = data;
235 pa_time_event *ev;
236 struct timeval tv;
237
238 pa_assert(d);
239 pa_assert(d->c);
240 pa_assert(timeout);
241
242 pa_assert_se(ev = dbus_timeout_get_data(timeout));
243
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));
246 } else
247 d->c->mainloop->time_restart(ev, pa_timeval_rtstore(&tv, PA_USEC_INVALID, d->c->use_rtclock));
248 }
249
250 static void wakeup_main(void *userdata) {
251 pa_dbus_wrap_connection *c = userdata;
252
253 pa_assert(c);
254
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);
258 }
259
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;
263 char *id;
264
265 pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
266
267 if (!(conn = dbus_bus_get_private(type, error)))
268 return NULL;
269
270 pconn = pa_xnew(pa_dbus_wrap_connection, 1);
271 pconn->mainloop = m;
272 pconn->connection = conn;
273 pconn->use_rtclock = use_rtclock;
274
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);
280
281 pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
282
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)));
287
288 dbus_free(id);
289
290 return pconn;
291 }
292
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;
295
296 pa_assert(m);
297 pa_assert(conn);
298
299 pconn = pa_xnew(pa_dbus_wrap_connection, 1);
300 pconn->mainloop = m;
301 pconn->connection = dbus_connection_ref(conn);
302 pconn->use_rtclock = use_rtclock;
303
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);
309
310 pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
311
312 return pconn;
313 }
314
315 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
316 pa_assert(c);
317
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))
323 ;
324 }
325
326 c->mainloop->defer_free(c->dispatch_event);
327 dbus_connection_unref(c->connection);
328 pa_xfree(c);
329 }
330
331 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
332 pa_assert(c);
333 pa_assert(c->connection);
334
335 return c->connection;
336 }
337
338 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
339 const char *t;
340 va_list ap;
341 unsigned k = 0;
342
343 pa_assert(c);
344 pa_assert(error);
345
346 va_start(ap, error);
347 while ((t = va_arg(ap, const char*))) {
348 dbus_bus_add_match(c, t, error);
349
350 if (dbus_error_is_set(error))
351 goto fail;
352
353 k++;
354 }
355 va_end(ap);
356 return 0;
357
358 fail:
359
360 va_end(ap);
361 va_start(ap, error);
362 for (; k > 0; k--) {
363 DBusError e;
364
365 pa_assert_se(t = va_arg(ap, const char*));
366
367 dbus_error_init(&e);
368 dbus_bus_remove_match(c, t, &e);
369 dbus_error_free(&e);
370 }
371 va_end(ap);
372
373 return -1;
374 }
375
376 void pa_dbus_remove_matches(DBusConnection *c, ...) {
377 const char *t;
378 va_list ap;
379 DBusError error;
380
381 pa_assert(c);
382
383 dbus_error_init(&error);
384
385 va_start(ap, c);
386 while ((t = va_arg(ap, const char*))) {
387 dbus_bus_remove_match(c, t, &error);
388 dbus_error_free(&error);
389 }
390 va_end(ap);
391 }
392
393 pa_dbus_pending *pa_dbus_pending_new(
394 DBusConnection *c,
395 DBusMessage *m,
396 DBusPendingCall *pending,
397 void *context_data,
398 void *call_data) {
399
400 pa_dbus_pending *p;
401
402 pa_assert(pending);
403
404 p = pa_xnew(pa_dbus_pending, 1);
405 p->connection = c;
406 p->message = m;
407 p->pending = pending;
408 p->context_data = context_data;
409 p->call_data = call_data;
410
411 PA_LLIST_INIT(pa_dbus_pending, p);
412
413 return p;
414 }
415
416 void pa_dbus_pending_free(pa_dbus_pending *p) {
417 pa_assert(p);
418
419 if (p->pending) {
420 dbus_pending_call_cancel(p->pending);
421 dbus_pending_call_unref(p->pending);
422 }
423
424 if (p->message)
425 dbus_message_unref(p->message);
426
427 pa_xfree(p);
428 }
429
430 void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
431 pa_assert(p);
432
433 while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
434 ;
435 }
436
437 void pa_dbus_free_pending_list(pa_dbus_pending **p) {
438 pa_dbus_pending *i;
439
440 pa_assert(p);
441
442 while ((i = *p)) {
443 PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
444 pa_dbus_pending_free(i);
445 }
446 }