]> code.delx.au - pulseaudio/blob - src/modules/dbus-util.c
merge 'lennart' branch back into trunk.
[pulseaudio] / src / modules / dbus-util.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2006 Lennart Poettering
7 Copyright 2006 Shams E. King
8
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2 of the License,
12 or (at your option) any later version.
13
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <pulse/xmalloc.h>
30 #include <pulse/timeval.h>
31 #include <pulsecore/log.h>
32 #include <pulsecore/props.h>
33
34 #include "dbus-util.h"
35
36 struct pa_dbus_connection {
37 PA_REFCNT_DECLARE;
38
39 pa_core *core;
40 DBusConnection *connection;
41 const char *property_name;
42 pa_defer_event* dispatch_event;
43 };
44
45 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
46 DBusConnection *conn = userdata;
47
48 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
49 /* no more data to process, disable the deferred */
50 ea->defer_enable(ev, 0);
51 }
52 }
53
54 /* DBusDispatchStatusFunction callback for the pa mainloop */
55 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
56 pa_dbus_connection *c = userdata;
57
58 pa_assert(c);
59
60 switch(status) {
61
62 case DBUS_DISPATCH_COMPLETE:
63 c->core->mainloop->defer_enable(c->dispatch_event, 0);
64 break;
65
66 case DBUS_DISPATCH_DATA_REMAINS:
67 case DBUS_DISPATCH_NEED_MEMORY:
68 default:
69 c->core->mainloop->defer_enable(c->dispatch_event, 1);
70 break;
71 }
72 }
73
74 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
75 unsigned int flags;
76 pa_io_event_flags_t events = 0;
77
78 pa_assert(watch);
79
80 flags = dbus_watch_get_flags(watch);
81
82 /* no watch flags for disabled watches */
83 if (!dbus_watch_get_enabled(watch))
84 return PA_IO_EVENT_NULL;
85
86 if (flags & DBUS_WATCH_READABLE)
87 events |= PA_IO_EVENT_INPUT;
88 if (flags & DBUS_WATCH_WRITABLE)
89 events |= PA_IO_EVENT_OUTPUT;
90
91 return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
92 }
93
94 /* pa_io_event_cb_t IO event handler */
95 static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
96 unsigned int flags = 0;
97 DBusWatch *watch = userdata;
98
99 #if HAVE_DBUS_WATCH_GET_UNIX_FD
100 pa_assert(fd == dbus_watch_get_unix_fd(watch));
101 #else
102 pa_assert(fd == dbus_watch_get_fd(watch));
103 #endif
104
105 if (!dbus_watch_get_enabled(watch)) {
106 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
107 return;
108 }
109
110 if (events & PA_IO_EVENT_INPUT)
111 flags |= DBUS_WATCH_READABLE;
112 if (events & PA_IO_EVENT_OUTPUT)
113 flags |= DBUS_WATCH_WRITABLE;
114 if (events & PA_IO_EVENT_HANGUP)
115 flags |= DBUS_WATCH_HANGUP;
116 if (events & PA_IO_EVENT_ERROR)
117 flags |= DBUS_WATCH_ERROR;
118
119 dbus_watch_handle(watch, flags);
120 }
121
122 /* pa_time_event_cb_t timer event handler */
123 static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
124 DBusTimeout *timeout = 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 pa_timeval_add(&next, dbus_timeout_get_interval(timeout) * 1000);
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 pa_core *c = PA_CORE(data);
139 pa_io_event *ev;
140
141 pa_assert(watch);
142 pa_assert(c);
143
144 ev = c->mainloop->io_new(
145 c->mainloop,
146 #if HAVE_DBUS_WATCH_GET_UNIX_FD
147 dbus_watch_get_unix_fd(watch),
148 #else
149 dbus_watch_get_fd(watch),
150 #endif
151 get_watch_flags(watch), handle_io_event, watch);
152
153 dbus_watch_set_data(watch, ev, NULL);
154
155 return TRUE;
156 }
157
158 /* DBusRemoveWatchFunction callback for pa mainloop */
159 static void remove_watch(DBusWatch *watch, void *data) {
160 pa_core *c = PA_CORE(data);
161 pa_io_event *ev;
162
163 pa_assert(watch);
164 pa_assert(c);
165
166 if ((ev = dbus_watch_get_data(watch)))
167 c->mainloop->io_free(ev);
168 }
169
170 /* DBusWatchToggledFunction callback for pa mainloop */
171 static void toggle_watch(DBusWatch *watch, void *data) {
172 pa_core *c = PA_CORE(data);
173 pa_io_event *ev;
174
175 pa_assert(watch);
176 pa_core_assert_ref(c);
177
178 pa_assert_se(ev = dbus_watch_get_data(watch));
179
180 /* get_watch_flags() checks if the watch is enabled */
181 c->mainloop->io_enable(ev, get_watch_flags(watch));
182 }
183
184 /* DBusAddTimeoutFunction callback for pa mainloop */
185 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
186 pa_core *c = PA_CORE(data);
187 pa_time_event *ev;
188 struct timeval tv;
189
190 pa_assert(timeout);
191 pa_assert(c);
192
193 if (!dbus_timeout_get_enabled(timeout))
194 return FALSE;
195
196 pa_gettimeofday(&tv);
197 pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
198
199 ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
200
201 dbus_timeout_set_data(timeout, ev, NULL);
202
203 return TRUE;
204 }
205
206 /* DBusRemoveTimeoutFunction callback for pa mainloop */
207 static void remove_timeout(DBusTimeout *timeout, void *data) {
208 pa_core *c = PA_CORE(data);
209 pa_time_event *ev;
210
211 pa_assert(timeout);
212 pa_assert(c);
213
214 if ((ev = dbus_timeout_get_data(timeout)))
215 c->mainloop->time_free(ev);
216 }
217
218 /* DBusTimeoutToggledFunction callback for pa mainloop */
219 static void toggle_timeout(DBusTimeout *timeout, void *data) {
220 pa_core *c = PA_CORE(data);
221 pa_time_event *ev;
222
223 pa_assert(timeout);
224 pa_assert(c);
225
226 pa_assert_se(ev = dbus_timeout_get_data(timeout));
227
228 if (dbus_timeout_get_enabled(timeout)) {
229 struct timeval tv;
230
231 pa_gettimeofday(&tv);
232 pa_timeval_add(&tv, dbus_timeout_get_interval(timeout) * 1000);
233
234 c->mainloop->time_restart(ev, &tv);
235 } else
236 c->mainloop->time_restart(ev, NULL);
237 }
238
239 static void wakeup_main(void *userdata) {
240 pa_dbus_connection *c = userdata;
241
242 pa_assert(c);
243
244 /* this will wakeup the mainloop and dispatch events, although
245 * it may not be the cleanest way of accomplishing it */
246 c->core->mainloop->defer_enable(c->dispatch_event, 1);
247 }
248
249 static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) {
250 pa_dbus_connection *pconn;
251
252 pconn = pa_xnew(pa_dbus_connection, 1);
253 PA_REFCNT_INIT(pconn);
254 pconn->core = c;
255 pconn->property_name = name;
256 pconn->connection = conn;
257 pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn);
258
259 pa_property_set(c, name, pconn);
260
261 return pconn;
262 }
263
264 DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
265 pa_assert(c);
266 pa_assert(PA_REFCNT_VALUE(c) > 0);
267 pa_assert(c->connection);
268
269 return c->connection;
270 }
271
272 void pa_dbus_connection_unref(pa_dbus_connection *c) {
273 pa_assert(c);
274 pa_assert(PA_REFCNT_VALUE(c) > 0);
275
276 if (PA_REFCNT_DEC(c) > 0)
277 return;
278
279 if (dbus_connection_get_is_connected(c->connection)) {
280 dbus_connection_close(c->connection);
281 /* must process remaining messages, bit of a kludge to handle
282 * both unload and shutdown */
283 while (dbus_connection_read_write_dispatch(c->connection, -1));
284 }
285
286 /* already disconnected, just free */
287 pa_property_remove(c->core, c->property_name);
288 c->core->mainloop->defer_free(c->dispatch_event);
289 dbus_connection_unref(c->connection);
290 pa_xfree(c);
291 }
292
293 pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
294 pa_assert(c);
295 pa_assert(PA_REFCNT_VALUE(c) > 0);
296
297 PA_REFCNT_INC(c);
298
299 return c;
300 }
301
302 pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
303
304 static const char *const prop_name[] = {
305 [DBUS_BUS_SESSION] = "dbus-connection-session",
306 [DBUS_BUS_SYSTEM] = "dbus-connection-system",
307 [DBUS_BUS_STARTER] = "dbus-connection-starter"
308 };
309 DBusConnection *conn;
310 pa_dbus_connection *pconn;
311
312 pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
313
314 if ((pconn = pa_property_get(c, prop_name[type])))
315 return pa_dbus_connection_ref(pconn);
316
317 if (!(conn = dbus_bus_get_private(type, error)))
318 return NULL;
319
320 pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
321
322 dbus_connection_set_exit_on_disconnect(conn, FALSE);
323 dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
324 dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL);
325 dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL);
326 dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
327
328 return pconn;
329 }