1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gmain.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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 * This file is based on dbus-gmain.c from dbus-glib with functions renamed
24 * and unnecessary code removed.
25 */
26
27 #include <config.h>
28 #include <dbus/dbus.h>
29 #include "glib.h"
30 #include <string.h>
31
32 #include <libintl.h>
33 #define _(x) dgettext (GETTEXT_PACKAGE, x)
34 #define N_(x) x
35
36 /*
37 * DBusGMessageQueue:
38 * A GSource subclass for dispatching DBusConnection messages.
39 * We need this on top of the IO handlers, because sometimes
40 * there are messages to dispatch queued up but no IO pending.
41 */
42 typedef struct
43 {
44 GSource source; /* the parent GSource */
45 DBusConnection *connection; /* the connection to dispatch */
46 } DBusGMessageQueue;
47
48 static gboolean message_queue_prepare (GSource *source,
49 gint *timeout);
50 static gboolean message_queue_check (GSource *source);
51 static gboolean message_queue_dispatch (GSource *source,
52 GSourceFunc callback,
53 gpointer user_data);
54
55 static const GSourceFuncs message_queue_funcs = {
56 message_queue_prepare,
57 message_queue_check,
58 message_queue_dispatch,
59 NULL
60 };
61
62 static gboolean
message_queue_prepare(GSource * source,gint * timeout)63 message_queue_prepare (GSource *source,
64 gint *timeout)
65 {
66 DBusConnection *connection = ((DBusGMessageQueue *)source)->connection;
67
68 *timeout = -1;
69
70 return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS);
71 }
72
73 static gboolean
message_queue_check(GSource * source)74 message_queue_check (GSource *source)
75 {
76 return FALSE;
77 }
78
79 static gboolean
message_queue_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)80 message_queue_dispatch (GSource *source,
81 GSourceFunc callback,
82 gpointer user_data)
83 {
84 DBusConnection *connection = ((DBusGMessageQueue *)source)->connection;
85
86 dbus_connection_ref (connection);
87
88 /* Only dispatch once - we don't want to starve other GSource */
89 dbus_connection_dispatch (connection);
90
91 dbus_connection_unref (connection);
92
93 return TRUE;
94 }
95
96 typedef struct
97 {
98 GMainContext *context; /* the main context */
99 GSList *ios; /* all IOHandler */
100 GSList *timeouts; /* all TimeoutHandler */
101 DBusConnection *connection; /* NULL if this is really for a server not a connection */
102 GSource *message_queue_source; /* DBusGMessageQueue */
103 } ConnectionSetup;
104
105
106 typedef struct
107 {
108 ConnectionSetup *cs;
109 GSource *source;
110 DBusWatch *watch;
111 } IOHandler;
112
113 typedef struct
114 {
115 ConnectionSetup *cs;
116 GSource *source;
117 DBusTimeout *timeout;
118 } TimeoutHandler;
119
120 dbus_int32_t _dbus_gmain_connection_slot = -1;
121 static dbus_int32_t server_slot = -1;
122
123 static ConnectionSetup*
connection_setup_new(GMainContext * context,DBusConnection * connection)124 connection_setup_new (GMainContext *context,
125 DBusConnection *connection)
126 {
127 ConnectionSetup *cs;
128
129 cs = g_new0 (ConnectionSetup, 1);
130
131 g_assert (context != NULL);
132
133 cs->context = context;
134 g_main_context_ref (cs->context);
135
136 if (connection)
137 {
138 cs->connection = connection;
139
140 cs->message_queue_source = g_source_new ((GSourceFuncs *) &message_queue_funcs,
141 sizeof (DBusGMessageQueue));
142 ((DBusGMessageQueue*)cs->message_queue_source)->connection = connection;
143 g_source_attach (cs->message_queue_source, cs->context);
144 }
145
146 return cs;
147 }
148
149 static void
io_handler_source_finalized(gpointer data)150 io_handler_source_finalized (gpointer data)
151 {
152 IOHandler *handler;
153
154 handler = data;
155
156 if (handler->watch)
157 dbus_watch_set_data (handler->watch, NULL, NULL);
158
159 g_free (handler);
160 }
161
162 static void
io_handler_destroy_source(void * data)163 io_handler_destroy_source (void *data)
164 {
165 IOHandler *handler;
166
167 handler = data;
168
169 if (handler->source)
170 {
171 GSource *source = handler->source;
172 handler->source = NULL;
173 handler->cs->ios = g_slist_remove (handler->cs->ios, handler);
174 g_source_destroy (source);
175 g_source_unref (source);
176 }
177 }
178
179 static void
io_handler_watch_freed(void * data)180 io_handler_watch_freed (void *data)
181 {
182 IOHandler *handler;
183
184 handler = data;
185
186 handler->watch = NULL;
187
188 io_handler_destroy_source (handler);
189 }
190
191 static gboolean
io_handler_dispatch(GIOChannel * source,GIOCondition condition,gpointer data)192 io_handler_dispatch (GIOChannel *source,
193 GIOCondition condition,
194 gpointer data)
195 {
196 IOHandler *handler;
197 guint dbus_condition = 0;
198 DBusConnection *connection;
199
200 handler = data;
201
202 connection = handler->cs->connection;
203
204 if (connection)
205 dbus_connection_ref (connection);
206
207 if (condition & G_IO_IN)
208 dbus_condition |= DBUS_WATCH_READABLE;
209 if (condition & G_IO_OUT)
210 dbus_condition |= DBUS_WATCH_WRITABLE;
211 if (condition & G_IO_ERR)
212 dbus_condition |= DBUS_WATCH_ERROR;
213 if (condition & G_IO_HUP)
214 dbus_condition |= DBUS_WATCH_HANGUP;
215
216 /* Note that we don't touch the handler after this, because
217 * dbus may have disabled the watch and thus killed the
218 * handler.
219 */
220 dbus_watch_handle (handler->watch, dbus_condition);
221 handler = NULL;
222
223 if (connection)
224 dbus_connection_unref (connection);
225
226 return TRUE;
227 }
228
229 /* Attach the connection setup to the given watch, removing any
230 * previously-attached connection setup.
231 */
232 static void
connection_setup_add_watch(ConnectionSetup * cs,DBusWatch * watch)233 connection_setup_add_watch (ConnectionSetup *cs,
234 DBusWatch *watch)
235 {
236 guint flags;
237 GIOCondition condition;
238 GIOChannel *channel;
239 IOHandler *handler;
240
241 if (!dbus_watch_get_enabled (watch))
242 return;
243
244 flags = dbus_watch_get_flags (watch);
245
246 condition = G_IO_ERR | G_IO_HUP;
247 if (flags & DBUS_WATCH_READABLE)
248 condition |= G_IO_IN;
249 if (flags & DBUS_WATCH_WRITABLE)
250 condition |= G_IO_OUT;
251
252 handler = g_new0 (IOHandler, 1);
253 handler->cs = cs;
254 handler->watch = watch;
255
256 channel = g_io_channel_unix_new (dbus_watch_get_unix_fd (watch));
257
258 handler->source = g_io_create_watch (channel, condition);
259 g_source_set_callback (handler->source, (GSourceFunc) io_handler_dispatch, handler,
260 io_handler_source_finalized);
261 g_source_attach (handler->source, cs->context);
262
263 cs->ios = g_slist_prepend (cs->ios, handler);
264
265 dbus_watch_set_data (watch, handler, io_handler_watch_freed);
266 g_io_channel_unref (channel);
267 }
268
269 static void
connection_setup_remove_watch(ConnectionSetup * cs,DBusWatch * watch)270 connection_setup_remove_watch (ConnectionSetup *cs,
271 DBusWatch *watch)
272 {
273 IOHandler *handler;
274
275 handler = dbus_watch_get_data (watch);
276
277 if (handler == NULL || handler->cs != cs)
278 return;
279
280 io_handler_destroy_source (handler);
281 }
282
283 static void
timeout_handler_source_finalized(gpointer data)284 timeout_handler_source_finalized (gpointer data)
285 {
286 TimeoutHandler *handler;
287
288 handler = data;
289
290 if (handler->timeout)
291 dbus_timeout_set_data (handler->timeout, NULL, NULL);
292
293 g_free (handler);
294 }
295
296 static void
timeout_handler_destroy_source(void * data)297 timeout_handler_destroy_source (void *data)
298 {
299 TimeoutHandler *handler;
300
301 handler = data;
302
303 if (handler->source)
304 {
305 GSource *source = handler->source;
306 handler->source = NULL;
307 handler->cs->timeouts = g_slist_remove (handler->cs->timeouts, handler);
308 g_source_destroy (source);
309 g_source_unref (source);
310 }
311 }
312
313 static void
timeout_handler_timeout_freed(void * data)314 timeout_handler_timeout_freed (void *data)
315 {
316 TimeoutHandler *handler;
317
318 handler = data;
319
320 handler->timeout = NULL;
321
322 timeout_handler_destroy_source (handler);
323 }
324
325 static gboolean
timeout_handler_dispatch(gpointer data)326 timeout_handler_dispatch (gpointer data)
327 {
328 TimeoutHandler *handler;
329
330 handler = data;
331
332 dbus_timeout_handle (handler->timeout);
333
334 return TRUE;
335 }
336
337 static void
connection_setup_add_timeout(ConnectionSetup * cs,DBusTimeout * timeout)338 connection_setup_add_timeout (ConnectionSetup *cs,
339 DBusTimeout *timeout)
340 {
341 TimeoutHandler *handler;
342
343 if (!dbus_timeout_get_enabled (timeout))
344 return;
345
346 handler = g_new0 (TimeoutHandler, 1);
347 handler->cs = cs;
348 handler->timeout = timeout;
349
350 handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
351 g_source_set_callback (handler->source, timeout_handler_dispatch, handler,
352 timeout_handler_source_finalized);
353 g_source_attach (handler->source, handler->cs->context);
354
355 cs->timeouts = g_slist_prepend (cs->timeouts, handler);
356
357 dbus_timeout_set_data (timeout, handler, timeout_handler_timeout_freed);
358 }
359
360 static void
connection_setup_remove_timeout(ConnectionSetup * cs,DBusTimeout * timeout)361 connection_setup_remove_timeout (ConnectionSetup *cs,
362 DBusTimeout *timeout)
363 {
364 TimeoutHandler *handler;
365
366 handler = dbus_timeout_get_data (timeout);
367
368 if (handler == NULL)
369 return;
370
371 timeout_handler_destroy_source (handler);
372 }
373
374 static void
connection_setup_free(ConnectionSetup * cs)375 connection_setup_free (ConnectionSetup *cs)
376 {
377 while (cs->ios)
378 io_handler_destroy_source (cs->ios->data);
379
380 while (cs->timeouts)
381 timeout_handler_destroy_source (cs->timeouts->data);
382
383 if (cs->message_queue_source)
384 {
385 GSource *source;
386
387 source = cs->message_queue_source;
388 cs->message_queue_source = NULL;
389
390 g_source_destroy (source);
391 g_source_unref (source);
392 }
393
394 g_main_context_unref (cs->context);
395 g_free (cs);
396 }
397
398 static dbus_bool_t
add_watch(DBusWatch * watch,gpointer data)399 add_watch (DBusWatch *watch,
400 gpointer data)
401 {
402 ConnectionSetup *cs;
403
404 cs = data;
405
406 connection_setup_add_watch (cs, watch);
407
408 return TRUE;
409 }
410
411 static void
remove_watch(DBusWatch * watch,gpointer data)412 remove_watch (DBusWatch *watch,
413 gpointer data)
414 {
415 ConnectionSetup *cs;
416
417 cs = data;
418
419 connection_setup_remove_watch (cs, watch);
420 }
421
422 static void
watch_toggled(DBusWatch * watch,void * data)423 watch_toggled (DBusWatch *watch,
424 void *data)
425 {
426 /* Because we just exit on OOM, enable/disable is
427 * no different from add/remove
428 */
429 if (dbus_watch_get_enabled (watch))
430 add_watch (watch, data);
431 else
432 remove_watch (watch, data);
433 }
434
435 static dbus_bool_t
add_timeout(DBusTimeout * timeout,void * data)436 add_timeout (DBusTimeout *timeout,
437 void *data)
438 {
439 ConnectionSetup *cs;
440
441 cs = data;
442
443 if (!dbus_timeout_get_enabled (timeout))
444 return TRUE;
445
446 connection_setup_add_timeout (cs, timeout);
447
448 return TRUE;
449 }
450
451 static void
remove_timeout(DBusTimeout * timeout,void * data)452 remove_timeout (DBusTimeout *timeout,
453 void *data)
454 {
455 ConnectionSetup *cs;
456
457 cs = data;
458
459 connection_setup_remove_timeout (cs, timeout);
460 }
461
462 static void
timeout_toggled(DBusTimeout * timeout,void * data)463 timeout_toggled (DBusTimeout *timeout,
464 void *data)
465 {
466 /* Because we just exit on OOM, enable/disable is
467 * no different from add/remove
468 */
469 if (dbus_timeout_get_enabled (timeout))
470 add_timeout (timeout, data);
471 else
472 remove_timeout (timeout, data);
473 }
474
475 static void
wakeup_main(void * data)476 wakeup_main (void *data)
477 {
478 ConnectionSetup *cs = data;
479
480 g_main_context_wakeup (cs->context);
481 }
482
483
484 /* Move to a new context */
485 static ConnectionSetup*
connection_setup_new_from_old(GMainContext * context,ConnectionSetup * old)486 connection_setup_new_from_old (GMainContext *context,
487 ConnectionSetup *old)
488 {
489 ConnectionSetup *cs;
490
491 g_assert (old->context != context);
492
493 cs = connection_setup_new (context, old->connection);
494
495 while (old->ios != NULL)
496 {
497 IOHandler *handler = old->ios->data;
498
499 connection_setup_add_watch (cs, handler->watch);
500 /* The old handler will be removed from old->ios as a side-effect */
501 }
502
503 while (old->timeouts != NULL)
504 {
505 TimeoutHandler *handler = old->timeouts->data;
506
507 connection_setup_add_timeout (cs, handler->timeout);
508 }
509
510 return cs;
511 }
512
513 /* @} */ /* End of GLib bindings internals */
514
515 /**
516 * atspi_dbus_connection_setup_with_g_main: (skip)
517 * @connection: the connection
518 * @context: the #GMainContext or #NULL for default context
519 *
520 * Sets the watch and timeout functions of a #DBusConnection
521 * to integrate the connection with the GLib main loop.
522 * Pass in #NULL for the #GMainContext unless you're
523 * doing something specialized.
524 *
525 * If called twice for the same context, does nothing the second
526 * time. If called once with context A and once with context B,
527 * context B replaces context A as the context monitoring the
528 * connection.
529 */
530 void
atspi_dbus_connection_setup_with_g_main(DBusConnection * connection,GMainContext * context)531 atspi_dbus_connection_setup_with_g_main (DBusConnection *connection,
532 GMainContext *context)
533 {
534 ConnectionSetup *old_setup;
535 ConnectionSetup *cs;
536
537 /* FIXME we never free the slot, so its refcount just keeps growing,
538 * which is kind of broken.
539 */
540 dbus_connection_allocate_data_slot (&_dbus_gmain_connection_slot);
541 if (_dbus_gmain_connection_slot < 0)
542 goto nomem;
543
544 if (context == NULL)
545 context = g_main_context_default ();
546
547 cs = NULL;
548
549 old_setup = dbus_connection_get_data (connection, _dbus_gmain_connection_slot);
550 if (old_setup != NULL)
551 {
552 if (old_setup->context == context)
553 return; /* nothing to do */
554
555 cs = connection_setup_new_from_old (context, old_setup);
556
557 /* Nuke the old setup */
558 dbus_connection_set_data (connection, _dbus_gmain_connection_slot, NULL, NULL);
559 old_setup = NULL;
560 }
561
562 if (cs == NULL)
563 cs = connection_setup_new (context, connection);
564
565 if (!dbus_connection_set_data (connection, _dbus_gmain_connection_slot, cs,
566 (DBusFreeFunction)connection_setup_free))
567 goto nomem;
568
569 if (!dbus_connection_set_watch_functions (connection,
570 add_watch,
571 remove_watch,
572 watch_toggled,
573 cs, NULL))
574 goto nomem;
575
576 if (!dbus_connection_set_timeout_functions (connection,
577 add_timeout,
578 remove_timeout,
579 timeout_toggled,
580 cs, NULL))
581 goto nomem;
582
583 dbus_connection_set_wakeup_main_function (connection,
584 wakeup_main,
585 cs, NULL);
586
587 return;
588
589 nomem:
590 g_error ("Not enough memory to set up DBusConnection for use with GLib");
591 }
592
593 /**
594 * atspi_dbus_server_setup_with_g_main: (skip)
595 * @server: the server
596 * @context: the #GMainContext or #NULL for default
597 *
598 * Sets the watch and timeout functions of a #DBusServer
599 * to integrate the server with the GLib main loop.
600 * In most cases the context argument should be #NULL.
601 *
602 * If called twice for the same context, does nothing the second
603 * time. If called once with context A and once with context B,
604 * context B replaces context A as the context monitoring the
605 * connection.
606 */
607 void
atspi_dbus_server_setup_with_g_main(DBusServer * server,GMainContext * context)608 atspi_dbus_server_setup_with_g_main (DBusServer *server,
609 GMainContext *context)
610 {
611 ConnectionSetup *old_setup;
612 ConnectionSetup *cs;
613
614 /* FIXME we never free the slot, so its refcount just keeps growing,
615 * which is kind of broken.
616 */
617 dbus_server_allocate_data_slot (&server_slot);
618 if (server_slot < 0)
619 goto nomem;
620
621 if (context == NULL)
622 context = g_main_context_default ();
623
624 cs = NULL;
625
626 old_setup = dbus_server_get_data (server, server_slot);
627 if (old_setup != NULL)
628 {
629 if (old_setup->context == context)
630 return; /* nothing to do */
631
632 cs = connection_setup_new_from_old (context, old_setup);
633
634 /* Nuke the old setup */
635 if (!dbus_server_set_data (server, server_slot, NULL, NULL))
636 goto nomem;
637 old_setup = NULL;
638 }
639
640 if (cs == NULL)
641 cs = connection_setup_new (context, NULL);
642
643 if (!dbus_server_set_data (server, server_slot, cs,
644 (DBusFreeFunction)connection_setup_free))
645 goto nomem;
646
647 if (!dbus_server_set_watch_functions (server,
648 add_watch,
649 remove_watch,
650 watch_toggled,
651 cs, NULL))
652 goto nomem;
653
654 if (!dbus_server_set_timeout_functions (server,
655 add_timeout,
656 remove_timeout,
657 timeout_toggled,
658 cs, NULL))
659 goto nomem;
660
661 return;
662
663 nomem:
664 g_error ("Not enough memory to set up DBusServer for use with GLib");
665 }
666