]> code.delx.au - pulseaudio/blob - src/pulsecore/dbus-util.c
aupdate: implicitly call _write_swap() if it wasn't called explicitly
[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 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
294 pa_assert(c);
295
296 if (dbus_connection_get_is_connected(c->connection)) {
297 dbus_connection_close(c->connection);
298 /* must process remaining messages, bit of a kludge to handle
299 * both unload and shutdown */
300 while (dbus_connection_read_write_dispatch(c->connection, -1))
301 ;
302 }
303
304 c->mainloop->defer_free(c->dispatch_event);
305 dbus_connection_unref(c->connection);
306 pa_xfree(c);
307 }
308
309 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
310 pa_assert(c);
311 pa_assert(c->connection);
312
313 return c->connection;
314 }
315
316 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
317 const char *t;
318 va_list ap;
319 unsigned k = 0;
320
321 pa_assert(c);
322 pa_assert(error);
323
324 va_start(ap, error);
325 while ((t = va_arg(ap, const char*))) {
326 dbus_bus_add_match(c, t, error);
327
328 if (dbus_error_is_set(error))
329 goto fail;
330
331 k++;
332 }
333 va_end(ap);
334 return 0;
335
336 fail:
337
338 va_end(ap);
339 va_start(ap, error);
340 for (; k > 0; k--) {
341 DBusError e;
342
343 pa_assert_se(t = va_arg(ap, const char*));
344
345 dbus_error_init(&e);
346 dbus_bus_remove_match(c, t, &e);
347 dbus_error_free(&e);
348 }
349 va_end(ap);
350
351 return -1;
352 }
353
354 void pa_dbus_remove_matches(DBusConnection *c, ...) {
355 const char *t;
356 va_list ap;
357 DBusError error;
358
359 pa_assert(c);
360
361 dbus_error_init(&error);
362
363 va_start(ap, c);
364 while ((t = va_arg(ap, const char*))) {
365 dbus_bus_remove_match(c, t, &error);
366 dbus_error_free(&error);
367 }
368 va_end(ap);
369 }
370
371 pa_dbus_pending *pa_dbus_pending_new(
372 DBusConnection *c,
373 DBusMessage *m,
374 DBusPendingCall *pending,
375 void *context_data,
376 void *call_data) {
377
378 pa_dbus_pending *p;
379
380 pa_assert(pending);
381
382 p = pa_xnew(pa_dbus_pending, 1);
383 p->connection = c;
384 p->message = m;
385 p->pending = pending;
386 p->context_data = context_data;
387 p->call_data = call_data;
388
389 PA_LLIST_INIT(pa_dbus_pending, p);
390
391 return p;
392 }
393
394 void pa_dbus_pending_free(pa_dbus_pending *p) {
395 pa_assert(p);
396
397 if (p->pending) {
398 dbus_pending_call_cancel(p->pending);
399 dbus_pending_call_unref(p->pending);
400 }
401
402 if (p->message)
403 dbus_message_unref(p->message);
404
405 pa_xfree(p);
406 }
407
408 void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
409 pa_assert(p);
410
411 while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
412 ;
413 }
414
415 void pa_dbus_free_pending_list(pa_dbus_pending **p) {
416 pa_dbus_pending *i;
417
418 pa_assert(p);
419
420 while ((i = *p)) {
421 PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
422 pa_dbus_pending_free(i);
423 }
424 }