]> code.delx.au - pulseaudio/blob - src/pulsecore/dbus-util.c
dbus: split dbus-util into dbus-shared
[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/xmalloc.h>
30 #include <pulse/timeval.h>
31 #include <pulsecore/log.h>
32
33 #include "dbus-util.h"
34
35 struct pa_dbus_wrap_connection {
36 pa_mainloop_api *mainloop;
37 DBusConnection *connection;
38 pa_defer_event* dispatch_event;
39 };
40
41 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
42 DBusConnection *conn = userdata;
43
44 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
45 /* no more data to process, disable the deferred */
46 ea->defer_enable(ev, 0);
47 }
48 }
49
50 /* DBusDispatchStatusFunction callback for the pa mainloop */
51 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
52 pa_dbus_wrap_connection *c = userdata;
53
54 pa_assert(c);
55
56 switch(status) {
57
58 case DBUS_DISPATCH_COMPLETE:
59 c->mainloop->defer_enable(c->dispatch_event, 0);
60 break;
61
62 case DBUS_DISPATCH_DATA_REMAINS:
63 case DBUS_DISPATCH_NEED_MEMORY:
64 default:
65 c->mainloop->defer_enable(c->dispatch_event, 1);
66 break;
67 }
68 }
69
70 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
71 unsigned int flags;
72 pa_io_event_flags_t events = 0;
73
74 pa_assert(watch);
75
76 flags = dbus_watch_get_flags(watch);
77
78 /* no watch flags for disabled watches */
79 if (!dbus_watch_get_enabled(watch))
80 return PA_IO_EVENT_NULL;
81
82 if (flags & DBUS_WATCH_READABLE)
83 events |= PA_IO_EVENT_INPUT;
84 if (flags & DBUS_WATCH_WRITABLE)
85 events |= PA_IO_EVENT_OUTPUT;
86
87 return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
88 }
89
90 /* pa_io_event_cb_t IO event handler */
91 static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
92 unsigned int flags = 0;
93 DBusWatch *watch = userdata;
94
95 #if HAVE_DBUS_WATCH_GET_UNIX_FD
96 pa_assert(fd == dbus_watch_get_unix_fd(watch));
97 #else
98 pa_assert(fd == dbus_watch_get_fd(watch));
99 #endif
100
101 if (!dbus_watch_get_enabled(watch)) {
102 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
103 return;
104 }
105
106 if (events & PA_IO_EVENT_INPUT)
107 flags |= DBUS_WATCH_READABLE;
108 if (events & PA_IO_EVENT_OUTPUT)
109 flags |= DBUS_WATCH_WRITABLE;
110 if (events & PA_IO_EVENT_HANGUP)
111 flags |= DBUS_WATCH_HANGUP;
112 if (events & PA_IO_EVENT_ERROR)
113 flags |= DBUS_WATCH_ERROR;
114
115 dbus_watch_handle(watch, flags);
116 }
117
118 /* pa_time_event_cb_t timer event handler */
119 static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
120 DBusTimeout *timeout = userdata;
121
122 if (dbus_timeout_get_enabled(timeout)) {
123 struct timeval next = *tv;
124 dbus_timeout_handle(timeout);
125
126 /* restart it for the next scheduled time */
127 pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
128 ea->time_restart(e, &next);
129 }
130 }
131
132 /* DBusAddWatchFunction callback for pa mainloop */
133 static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
134 pa_dbus_wrap_connection *c = data;
135 pa_io_event *ev;
136
137 pa_assert(watch);
138 pa_assert(c);
139
140 ev = c->mainloop->io_new(
141 c->mainloop,
142 #if HAVE_DBUS_WATCH_GET_UNIX_FD
143 dbus_watch_get_unix_fd(watch),
144 #else
145 dbus_watch_get_fd(watch),
146 #endif
147 get_watch_flags(watch), handle_io_event, watch);
148
149 dbus_watch_set_data(watch, ev, NULL);
150
151 return TRUE;
152 }
153
154 /* DBusRemoveWatchFunction callback for pa mainloop */
155 static void remove_watch(DBusWatch *watch, void *data) {
156 pa_dbus_wrap_connection *c = data;
157 pa_io_event *ev;
158
159 pa_assert(watch);
160 pa_assert(c);
161
162 if ((ev = dbus_watch_get_data(watch)))
163 c->mainloop->io_free(ev);
164 }
165
166 /* DBusWatchToggledFunction callback for pa mainloop */
167 static void toggle_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 pa_assert_se(ev = dbus_watch_get_data(watch));
175
176 /* get_watch_flags() checks if the watch is enabled */
177 c->mainloop->io_enable(ev, get_watch_flags(watch));
178 }
179
180 /* DBusAddTimeoutFunction callback for pa mainloop */
181 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
182 pa_dbus_wrap_connection *c = data;
183 pa_time_event *ev;
184 struct timeval tv;
185
186 pa_assert(timeout);
187 pa_assert(c);
188
189 if (!dbus_timeout_get_enabled(timeout))
190 return FALSE;
191
192 pa_gettimeofday(&tv);
193 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
194
195 ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
196
197 dbus_timeout_set_data(timeout, ev, NULL);
198
199 return TRUE;
200 }
201
202 /* DBusRemoveTimeoutFunction callback for pa mainloop */
203 static void remove_timeout(DBusTimeout *timeout, void *data) {
204 pa_dbus_wrap_connection *c = data;
205 pa_time_event *ev;
206
207 pa_assert(timeout);
208 pa_assert(c);
209
210 if ((ev = dbus_timeout_get_data(timeout)))
211 c->mainloop->time_free(ev);
212 }
213
214 /* DBusTimeoutToggledFunction callback for pa mainloop */
215 static void toggle_timeout(DBusTimeout *timeout, void *data) {
216 pa_dbus_wrap_connection *c = data;
217 pa_time_event *ev;
218
219 pa_assert(timeout);
220 pa_assert(c);
221
222 pa_assert_se(ev = dbus_timeout_get_data(timeout));
223
224 if (dbus_timeout_get_enabled(timeout)) {
225 struct timeval tv;
226
227 pa_gettimeofday(&tv);
228 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
229
230 c->mainloop->time_restart(ev, &tv);
231 } else
232 c->mainloop->time_restart(ev, NULL);
233 }
234
235 static void wakeup_main(void *userdata) {
236 pa_dbus_wrap_connection *c = userdata;
237
238 pa_assert(c);
239
240 /* this will wakeup the mainloop and dispatch events, although
241 * it may not be the cleanest way of accomplishing it */
242 c->mainloop->defer_enable(c->dispatch_event, 1);
243 }
244
245 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) {
246 DBusConnection *conn;
247 pa_dbus_wrap_connection *pconn = NULL;
248
249 pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
250
251 if (!(conn = dbus_bus_get_private(type, error)))
252 return NULL;
253
254 pconn = pa_xnew(pa_dbus_wrap_connection, 1);
255 pconn->mainloop = m;
256 pconn->connection = conn;
257
258 dbus_connection_set_exit_on_disconnect(conn, FALSE);
259 dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
260 dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
261 dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
262 dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
263
264 pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
265
266 return pconn;
267 }
268
269 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
270 pa_assert(c);
271
272 if (dbus_connection_get_is_connected(c->connection)) {
273 dbus_connection_close(c->connection);
274 /* must process remaining messages, bit of a kludge to handle
275 * both unload and shutdown */
276 while (dbus_connection_read_write_dispatch(c->connection, -1));
277 }
278
279 c->mainloop->defer_free(c->dispatch_event);
280 dbus_connection_unref(c->connection);
281 pa_xfree(c);
282 }
283
284 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
285 pa_assert(c);
286 pa_assert(c->connection);
287
288 return c->connection;
289 }
290
291 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
292 const char *t;
293 va_list ap;
294 unsigned k = 0;
295
296 pa_assert(c);
297 pa_assert(error);
298
299 va_start(ap, error);
300 while ((t = va_arg(ap, const char*))) {
301 dbus_bus_add_match(c, t, error);
302
303 if (dbus_error_is_set(error))
304 goto fail;
305
306 k++;
307 }
308 va_end(ap);
309 return 0;
310
311 fail:
312
313 va_end(ap);
314 va_start(ap, error);
315 for (; k > 0; k--) {
316 DBusError e;
317
318 pa_assert_se(t = va_arg(ap, const char*));
319
320 dbus_error_init(&e);
321 dbus_bus_remove_match(c, t, &e);
322 dbus_error_free(&e);
323 }
324 va_end(ap);
325
326 return -1;
327 }
328
329 void pa_dbus_remove_matches(DBusConnection *c, ...) {
330 const char *t;
331 va_list ap;
332 DBusError error;
333
334 pa_assert(c);
335
336 dbus_error_init(&error);
337
338 va_start(ap, c);
339 while ((t = va_arg(ap, const char*))) {
340 dbus_bus_remove_match(c, t, &error);
341 dbus_error_free(&error);
342 }
343 va_end(ap);
344 }
345
346 pa_dbus_pending *pa_dbus_pending_new(
347 DBusConnection *c,
348 DBusMessage *m,
349 DBusPendingCall *pending,
350 void *context_data,
351 void *call_data) {
352
353 pa_dbus_pending *p;
354
355 pa_assert(pending);
356
357 p = pa_xnew(pa_dbus_pending, 1);
358 p->connection = c;
359 p->message = m;
360 p->pending = pending;
361 p->context_data = context_data;
362 p->call_data = call_data;
363
364 PA_LLIST_INIT(pa_dbus_pending, p);
365
366 return p;
367 }
368
369 void pa_dbus_pending_free(pa_dbus_pending *p) {
370 pa_assert(p);
371
372 if (p->pending)
373 dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */
374
375 if (p->message)
376 dbus_message_unref(p->message);
377
378 pa_xfree(p);
379 }
380
381 void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
382 pa_assert(p);
383
384 while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
385 ;
386 }
387
388 void pa_dbus_free_pending_list(pa_dbus_pending **p) {
389 pa_dbus_pending *i;
390
391 pa_assert(p);
392
393 while ((i = *p)) {
394 PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
395 pa_dbus_pending_free(i);
396 }
397 }