b5c613372d04aa6735f1b6cb334902c78d8bf10f
[platform/upstream/pulseaudio.git] / src / modules / dbus-util.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5  
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 of the License,
9   or (at your option) any later version.
10  
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.
15  
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
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
27 #include <pulsecore/log.h>
28 #include <pulsecore/props.h>
29 #include <pulse/xmalloc.h>
30
31 #include "dbus-util.h"
32
33 struct pa_dbus_connection {
34     int refcount;
35     pa_core *core;
36     DBusConnection *connection;
37     const char *property_name;
38     pa_defer_event* dispatch_event;
39 };
40
41 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata)
42 {
43     DBusConnection *conn = (DBusConnection *) userdata;
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,
52                             void *userdata)
53 {
54     pa_dbus_connection *c = (pa_dbus_connection*) userdata;
55     switch(status) {
56         case DBUS_DISPATCH_COMPLETE:
57             c->core->mainloop->defer_enable(c->dispatch_event, 0);
58             break;
59         case DBUS_DISPATCH_DATA_REMAINS:
60         case DBUS_DISPATCH_NEED_MEMORY:
61         default:
62             c->core->mainloop->defer_enable(c->dispatch_event, 1);
63             break;
64     }
65 }
66
67 static pa_io_event_flags_t
68 get_watch_flags(DBusWatch *watch)
69 {
70     unsigned int flags = dbus_watch_get_flags(watch);
71     pa_io_event_flags_t events = PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
72
73     /* no watch flags for disabled watches */
74     if (!dbus_watch_get_enabled(watch))
75         return PA_IO_EVENT_NULL;
76
77     if (flags & DBUS_WATCH_READABLE)
78         events |= PA_IO_EVENT_INPUT;
79     if (flags & DBUS_WATCH_WRITABLE)
80         events |= PA_IO_EVENT_OUTPUT;
81
82     return events;
83 }
84
85 static void timeval_next(struct timeval *tv, int millint)
86 {
87     /* number of seconds in the milli-second interval */
88     tv->tv_sec += (millint / 1000);
89     /* milliseconds minus the seconds portion, converted to microseconds */
90     tv->tv_usec += (millint - tv->tv_sec * 1000) * 1000;
91 }
92
93 /* pa_io_event_cb_t IO event handler */
94 static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e,
95                             int fd, pa_io_event_flags_t events, void *userdata)
96 {
97     unsigned int flags = 0;
98     DBusWatch *watch = (DBusWatch*) userdata;
99
100     assert(fd == dbus_watch_get_fd(watch));
101
102     if (!dbus_watch_get_enabled(watch)) {
103         pa_log_warn(__FILE__": Asked to handle disabled watch: %p %i",
104                     (void *) watch, fd);
105         return;
106     }
107
108     if (events & PA_IO_EVENT_INPUT)
109         flags |= DBUS_WATCH_READABLE;
110     if (events & PA_IO_EVENT_OUTPUT)
111         flags |= DBUS_WATCH_WRITABLE;
112     if (events & PA_IO_EVENT_HANGUP)
113         flags |= DBUS_WATCH_HANGUP;
114     if (events & PA_IO_EVENT_ERROR)
115         flags |= DBUS_WATCH_ERROR;
116
117     dbus_watch_handle(watch, flags);
118 }
119
120 /* pa_time_event_cb_t timer event handler */
121 static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e,
122                               const struct timeval *tv, void *userdata)
123 {
124     DBusTimeout *timeout = (DBusTimeout*) userdata;
125
126     if (dbus_timeout_get_enabled(timeout)) {
127         struct timeval next = *tv;
128         dbus_timeout_handle(timeout);
129
130         /* restart it for the next scheduled time */
131         timeval_next(&next, dbus_timeout_get_interval(timeout));
132         ea->time_restart(e, &next);
133     }
134 }
135
136 /* DBusAddWatchFunction callback for pa mainloop */
137 static dbus_bool_t add_watch(DBusWatch *watch, void *data)
138 {
139     pa_io_event *ev;
140     pa_core *c = (pa_core*) data;
141
142     ev = c->mainloop->io_new(c->mainloop, dbus_watch_get_fd(watch),
143                              get_watch_flags(watch),
144                              handle_io_event, (void*) watch);
145     if (NULL == ev)
146         return FALSE;
147
148     /* dbus_watch_set_data(watch, (void*) ev, c->mainloop->io_free); */
149     dbus_watch_set_data(watch, (void*) ev, NULL);
150
151     return TRUE;
152 }
153
154 /* DBusRemoveWatchFunction callback for pa mainloop */
155 static void remove_watch(DBusWatch *watch, void *data)
156 {
157     pa_core *c = (pa_core*) data;
158     pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
159
160     /* free the event */
161     if (NULL != ev)
162         c->mainloop->io_free(ev);
163 }
164
165 /* DBusWatchToggledFunction callback for pa mainloop */
166 static void toggle_watch(DBusWatch *watch, void *data)
167 {
168     pa_core *c = (pa_core*) data;
169     pa_io_event *ev = (pa_io_event*) dbus_watch_get_data(watch);
170
171     /* get_watch_flags() checks if the watch is enabled */
172     c->mainloop->io_enable(ev, get_watch_flags(watch));
173 }
174
175 /* DBusAddTimeoutFunction callback for pa mainloop */
176 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
177 {
178     struct timeval tv;
179     pa_time_event *ev;
180     pa_core *c = (pa_core*) data;
181
182     if (!dbus_timeout_get_enabled(timeout))
183         return FALSE;
184
185     if (gettimeofday(&tv, NULL) < 0)
186         return -1;
187
188     timeval_next(&tv, dbus_timeout_get_interval(timeout));
189
190     ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event,
191                                (void*) timeout);
192     if (NULL == ev)
193         return FALSE;
194
195     /* dbus_timeout_set_data(timeout, (void*) ev, c->mainloop->time_free); */
196     dbus_timeout_set_data(timeout, (void*) ev, NULL);
197
198     return TRUE;
199 }
200
201 /* DBusRemoveTimeoutFunction callback for pa mainloop */
202 static void remove_timeout(DBusTimeout *timeout, void *data)
203 {
204     pa_core *c = (pa_core*) data;
205     pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
206
207     /* free the event */
208     if (NULL != ev)
209         c->mainloop->time_free(ev);
210 }
211
212 /* DBusTimeoutToggledFunction callback for pa mainloop */
213 static void toggle_timeout(DBusTimeout *timeout, void *data)
214 {
215     struct timeval tv;
216     pa_core *c = (pa_core*) data;
217     pa_time_event *ev = (pa_time_event*) dbus_timeout_get_data(timeout);
218
219     gettimeofday(&tv, NULL);
220     if (dbus_timeout_get_enabled(timeout)) {
221         timeval_next(&tv, dbus_timeout_get_interval(timeout));
222         c->mainloop->time_restart(ev, &tv);
223     } else {
224         /* set it to expire one second ago */
225         tv.tv_sec -= 1;
226         c->mainloop->time_restart(ev, &tv);
227     }
228 }
229
230 static void
231 pa_dbus_connection_free(pa_dbus_connection *c)
232 {
233     assert(c);
234     assert(!dbus_connection_get_is_connected(c->connection));
235
236     /* already disconnected, just free */
237     pa_property_remove(c->core, c->property_name);
238     c->core->mainloop->defer_free(c->dispatch_event);
239     dbus_connection_unref(c->connection);
240     pa_xfree(c);
241 }
242
243 static void
244 wakeup_main(void *userdata)
245 {
246     pa_dbus_connection *c = (pa_dbus_connection*) userdata;
247     /* this will wakeup the mainloop and dispatch events, although
248      * it may not be the cleanest way of accomplishing it */
249     c->core->mainloop->defer_enable(c->dispatch_event, 1);
250 }
251
252 static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name)
253 {
254     pa_dbus_connection *pconn = pa_xmalloc(sizeof(pa_dbus_connection));
255
256     pconn->refcount = 1;
257     pconn->core = c;
258     pconn->property_name = name;
259     pconn->connection = conn;
260     pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb,
261                                                    (void*) conn);
262
263     pa_property_set(c, name, pconn);
264
265     return pconn;
266 }
267
268 DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c)
269 {
270     assert(c && c->connection);
271     return c->connection;
272 }
273
274 void pa_dbus_connection_unref(pa_dbus_connection *c)
275 {
276     assert(c);
277
278     /* non-zero refcount, still outstanding refs */
279     if (--(c->refcount))
280         return;
281
282     /* refcount is zero */
283     if (dbus_connection_get_is_connected(c->connection)) {
284         /* disconnect as we have no more internal references */
285         dbus_connection_close(c->connection);
286         /* must process remaining messages, bit of a kludge to
287          * handle both unload and shutdown */
288         while(dbus_connection_read_write_dispatch(c->connection, -1));
289     }
290     pa_dbus_connection_free(c);
291 }
292
293 pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c)
294 {
295     assert(c);
296
297     ++(c->refcount);
298
299     return c;
300 }
301
302 pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type,
303                                     DBusError *error)
304 {
305     const char* name;
306     DBusConnection *conn;
307     pa_dbus_connection *pconn;
308     static const char sysname[] = "dbus-connection-system";
309     static const char sessname[] = "dbus-connection-session";
310     static const char startname[] = "dbus-connection-starter";
311
312     switch (type) {
313         case DBUS_BUS_SYSTEM:
314             name = sysname;
315             break;
316         case DBUS_BUS_SESSION:
317             name = sessname;
318             break;
319         case DBUS_BUS_STARTER:
320             name = startname;
321             break;
322         default:
323             assert(0); /* never reached */
324             break;
325     }
326
327     if ((pconn = pa_property_get(c, name)))
328         return pa_dbus_connection_ref(pconn);
329
330     /* else */
331     conn = dbus_bus_get_private(type, error);
332     if (conn == NULL || dbus_error_is_set(error)) {
333         return NULL;
334     }
335
336     pconn = pa_dbus_connection_new(c, conn, name);
337
338     /* don't exit on disconnect */
339     dbus_connection_set_exit_on_disconnect(conn, FALSE);
340     /* set up the DBUS call backs */
341     dbus_connection_set_dispatch_status_function(conn, dispatch_status,
342                                                  (void*) pconn, NULL);
343     dbus_connection_set_watch_functions(conn,
344                                         add_watch,
345                                         remove_watch,
346                                         toggle_watch,
347                                         (void*) c, NULL);
348     dbus_connection_set_timeout_functions(conn,
349                                           add_timeout,
350                                           remove_timeout,
351                                           toggle_timeout,
352                                           (void*) c, NULL);
353     dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
354
355     return pconn;
356 }