1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* egg-dbus.c GLib main loop integration
3 *
4 * Copyright (C) 2002, 2003 CodeFactory AB
5 * Copyright (C) 2005 Red Hat, Inc.
6 *
7 * Licensed under the Academic Free License version 2.1
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25
26 #include "config.h"
27
28 #include "egg-dbus.h"
29
30 #include <dbus/dbus.h>
31
32 /*
33 * dbus_watch_get_unix_fd() is introduced in dbus 1.1.1, deprecating
34 * dbus_watch_get_fd(). We still need to use the old function for official
35 * GNOME 2.20, anyway. See Bug #465936.
36 */
37 #define VER_LESS_THAN(MAJOR, MINOR, MICRO, J, N, C) \
38 (MAJOR < J || (MAJOR == J && (MINOR < N || (MINOR == N && MICRO < C))))
strip_trailoptnull39 #if VER_LESS_THAN(GKR_DBUS_MAJOR_VERSION, GKR_DBUS_MINOR_VERSION, GKR_DBUS_MICRO_VERSION, 1, 1, 1)
40 # define dbus_watch_get_unix_fd dbus_watch_get_fd
41 #endif
42
43 /* ------------------------------------------------------------------------
44 * DBUS GLIB MAIN LOOP INTEGRATION
45 *
46 * Copied from dbus-gmain.c due to API instabilities in dbus-glib bindings. :(
47 */
48
49 typedef struct {
50 GSource source; /* the parent GSource */
51 DBusConnection *connection; /* the connection to dispatch */
52 } DBusGMessageQueue;
53
54 static gboolean
55 message_queue_prepare (GSource *source, gint *timeout)
56 {
57 DBusConnection *connection = ((DBusGMessageQueue *)source)->connection;
58 *timeout = -1;
59 return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS);
60 }
61
62 static gboolean
63 message_queue_check (GSource *source)
64 {
65 return FALSE;
66 }
67
68 static gboolean
69 message_queue_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
70 {
71 DBusConnection *connection = ((DBusGMessageQueue *)source)->connection;
72 dbus_connection_ref (connection);
73
74 /* Only dispatch once - we don't want to starve other GSource */
75 dbus_connection_dispatch (connection);
76 dbus_connection_unref (connection);
77 return TRUE;
78 }
79
80 static const GSourceFuncs message_queue_funcs = {
81 message_queue_prepare,
82 message_queue_check,
83 message_queue_dispatch,
84 NULL
85 };
86
87 typedef struct {
88 GMainContext *context; /* the main context */
89 GSList *ios; /* all IOHandler */
90 GSList *timeouts; /* all TimeoutHandler */
91 DBusConnection *connection; /* NULL if this is really for a server not a connection */
92 GSource *message_queue_source; /* DBusGMessageQueue */
93 } ConnectionSetup;
94
95 static ConnectionSetup *the_setup = NULL;
96
97 typedef struct {
98 ConnectionSetup *cs;
99 GSource *source;
100 DBusWatch *watch;
101 } IOHandler;
102
103 typedef struct {
104 ConnectionSetup *cs;
105 GSource *source;
106 DBusTimeout *timeout;
107 } TimeoutHandler;
108
109 static ConnectionSetup*
110 connection_setup_new (GMainContext *context, DBusConnection *connection)
111 {
112 ConnectionSetup *cs = g_new0 (ConnectionSetup, 1);
113 g_assert (context != NULL);
114
115 cs->context = context;
116 g_main_context_ref (cs->context);
117
118 if (connection) {
119 cs->connection = connection;
120 cs->message_queue_source = g_source_new ((GSourceFuncs *) &message_queue_funcs,
121 sizeof (DBusGMessageQueue));
122 ((DBusGMessageQueue*)cs->message_queue_source)->connection = connection;
123 g_source_attach (cs->message_queue_source, cs->context);
124 }
125
126 return cs;
127 }
128
129 static void
130 io_handler_source_finalized (gpointer data)
131 {
132 IOHandler *handler = data;
133 if (handler->watch)
134 dbus_watch_set_data (handler->watch, NULL, NULL);
135 g_free (handler);
136 }
137
138 static void
139 io_handler_destroy_source (void *data)
140 {
141 IOHandler *handler = data;
142 if (handler->source) {
143 GSource *source = handler->source;
144 handler->source = NULL;
145 handler->cs->ios = g_slist_remove (handler->cs->ios, handler);
146 g_source_destroy (source);
147 g_source_unref (source);
148 }
149 }
150
151 static void
152 io_handler_watch_freed (void *data)
153 {
154 IOHandler *handler = data;
155 handler->watch = NULL;
156 io_handler_destroy_source (handler);
157 }
158
159 static gboolean
160 io_handler_dispatch (GIOChannel *source, GIOCondition condition, gpointer data)
161 {
162 IOHandler *handler = data;
163 guint dbus_condition = 0;
164 DBusConnection *connection = handler->cs->connection;
165
166 if (connection)
167 dbus_connection_ref (connection);
168
169 if (condition & G_IO_IN)
170 dbus_condition |= DBUS_WATCH_READABLE;
171 if (condition & G_IO_OUT)
172 dbus_condition |= DBUS_WATCH_WRITABLE;
173 if (condition & G_IO_ERR)
174 dbus_condition |= DBUS_WATCH_ERROR;
175 if (condition & G_IO_HUP)
176 dbus_condition |= DBUS_WATCH_HANGUP;
177
178 /* Note that we don't touch the handler after this, because
179 * dbus may have disabled the watch and thus killed the
180 * handler.
181 */
182 dbus_watch_handle (handler->watch, dbus_condition);
183 handler = NULL;
184
185 if (connection)
186 dbus_connection_unref (connection);
187
188 return TRUE;
189 }
190
191 static void
192 connection_setup_add_watch (ConnectionSetup *cs, DBusWatch *watch)
193 {
194 guint flags;
195 GIOCondition condition;
196 GIOChannel *channel;
197 IOHandler *handler;
198
199 if (!dbus_watch_get_enabled (watch))
200 return;
201
202 g_assert (dbus_watch_get_data (watch) == NULL);
203
204 flags = dbus_watch_get_flags (watch);
205
206 condition = G_IO_ERR | G_IO_HUP;
207 if (flags & DBUS_WATCH_READABLE)
208 condition |= G_IO_IN;
209 if (flags & DBUS_WATCH_WRITABLE)
210 condition |= G_IO_OUT;
211
212 handler = g_new0 (IOHandler, 1);
213 handler->cs = cs;
214 handler->watch = watch;
215
216 channel = g_io_channel_unix_new (dbus_watch_get_unix_fd (watch));
217
218 handler->source = g_io_create_watch (channel, condition);
219 g_source_set_callback (handler->source, (GSourceFunc) io_handler_dispatch, handler,
220 io_handler_source_finalized);
221 g_source_attach (handler->source, cs->context);
222
223 cs->ios = g_slist_prepend (cs->ios, handler);
224
225 dbus_watch_set_data (watch, handler, io_handler_watch_freed);
226 g_io_channel_unref (channel);
227 }
228
229 static void
230 connection_setup_remove_watch (ConnectionSetup *cs, DBusWatch *watch)
231 {
232 IOHandler *handler = dbus_watch_get_data (watch);
233 if (handler != NULL)
234 io_handler_destroy_source (handler);
235 }
236
237 static void
238 timeout_handler_source_finalized (gpointer data)
239 {
240 TimeoutHandler *handler = data;
241 if (handler->timeout)
242 dbus_timeout_set_data (handler->timeout, NULL, NULL);
243 g_free (handler);
244 }
245
246 static void
247 timeout_handler_destroy_source (void *data)
248 {
249 TimeoutHandler *handler = data;
250 if (handler->source) {
251 GSource *source = handler->source;
252 handler->source = NULL;
253 handler->cs->timeouts = g_slist_remove (handler->cs->timeouts, handler);
254 g_source_destroy (source);
255 g_source_unref (source);
256 }
257 }
258
259 static void
260 timeout_handler_timeout_freed (void *data)
261 {
262 TimeoutHandler *handler = data;
263 handler->timeout = NULL;
264 timeout_handler_destroy_source (handler);
265 }
266
267 static gboolean
268 timeout_handler_dispatch (gpointer data)
269 {
270 TimeoutHandler *handler = data;
271 dbus_timeout_handle (handler->timeout);
272 return TRUE;
273 }
274
275 static void
276 connection_setup_add_timeout (ConnectionSetup *cs,
277 DBusTimeout *timeout)
278 {
279 TimeoutHandler *handler;
280 if (!dbus_timeout_get_enabled (timeout))
281 return;
282 g_assert (dbus_timeout_get_data (timeout) == NULL);
283
284 handler = g_new0 (TimeoutHandler, 1);
285 handler->cs = cs;
286 handler->timeout = timeout;
287
288 handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
289 g_source_set_callback (handler->source, timeout_handler_dispatch, handler,
290 timeout_handler_source_finalized);
291 g_source_attach (handler->source, handler->cs->context);
292 cs->timeouts = g_slist_prepend (cs->timeouts, handler);
293 dbus_timeout_set_data (timeout, handler, timeout_handler_timeout_freed);
294 }
295
296 static void
297 connection_setup_remove_timeout (ConnectionSetup *cs, DBusTimeout *timeout)
298 {
299 TimeoutHandler *handler = dbus_timeout_get_data (timeout);
300 if (handler != NULL)
301 timeout_handler_destroy_source (handler);
302 }
303
304 static void
305 connection_setup_free (ConnectionSetup *cs)
306 {
307 while (cs->ios)
308 io_handler_destroy_source (cs->ios->data);
309
310 while (cs->timeouts)
311 timeout_handler_destroy_source (cs->timeouts->data);
312
313 if (cs->message_queue_source) {
314 GSource *source = cs->message_queue_source;
315 cs->message_queue_source = NULL;
316
317 g_source_destroy (source);
318 g_source_unref (source);
319 }
320
321 g_main_context_unref (cs->context);
322 g_free (cs);
323 }
324
325 static dbus_bool_t
326 add_watch (DBusWatch *watch, gpointer data)
327 {
328 ConnectionSetup *cs = data;
329 connection_setup_add_watch (cs, watch);
330 return TRUE;
331 }
332
333 static void
334 remove_watch (DBusWatch *watch, gpointer data)
335 {
336 ConnectionSetup *cs = data;
337 connection_setup_remove_watch (cs, watch);
338 }
339
340 static void
341 watch_toggled (DBusWatch *watch, void *data)
342 {
343 if (dbus_watch_get_enabled (watch))
344 add_watch (watch, data);
345 else
346 remove_watch (watch, data);
347 }
348
349 static dbus_bool_t
350 add_timeout (DBusTimeout *timeout, void *data)
351 {
352 ConnectionSetup *cs = data;
353 if (!dbus_timeout_get_enabled (timeout))
354 return TRUE;
355 connection_setup_add_timeout (cs, timeout);
356 return TRUE;
357 }
358
359 static void
360 remove_timeout (DBusTimeout *timeout, void *data)
361 {
362 ConnectionSetup *cs = data;
363 connection_setup_remove_timeout (cs, timeout);
364 }
365
366 static void
367 timeout_toggled (DBusTimeout *timeout, void *data)
368 {
369 if (dbus_timeout_get_enabled (timeout))
370 add_timeout (timeout, data);
371 else
372 remove_timeout (timeout, data);
373 }
374
375 static void
376 wakeup_main (void *data)
377 {
378 ConnectionSetup *cs = data;
379 g_main_context_wakeup (cs->context);
380 }
381
382 void
383 egg_dbus_connect_with_mainloop (DBusConnection *connection, GMainContext *context)
384 {
385 ConnectionSetup *cs;
386
387 if (context == NULL)
388 context = g_main_context_default ();
389 cs = connection_setup_new (context, connection);
390 the_setup = cs;
391
392 if (!dbus_connection_set_watch_functions (connection, add_watch,
393 remove_watch, watch_toggled,
394 cs, NULL))
395 goto nomem;
396
397 if (!dbus_connection_set_timeout_functions (connection, add_timeout,
398 remove_timeout, timeout_toggled,
399 cs, NULL))
400 goto nomem;
401
402 dbus_connection_set_wakeup_main_function (connection, wakeup_main, cs, NULL);
403
404 return;
405
406 nomem:
407 g_error ("Not enough memory to set up DBusConnection for use with GLib");
408 }
409
410 void
411 egg_dbus_disconnect_from_mainloop (DBusConnection *connection, GMainContext *context)
412 {
413 ConnectionSetup *cs = the_setup;
414 the_setup = NULL;
415
416 if (cs)
417 connection_setup_free (cs);
418 }
419