Fix some warnings and documentation issues
[platform/upstream/at-spi2-core.git] / atspi / atspi-gmain.c
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 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
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
74 message_queue_check (GSource *source)
75 {
76   return FALSE;
77 }
78
79 static gboolean
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*
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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*
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 /** @addtogroup DBusGLib
516  * @{
517  */
518
519 /**
520  * atspi_dbus_connection_setup_with_g_main:
521  * @connection: the connection
522  * @context: the #GMainContext or #NULL for default context
523  *
524  * Sets the watch and timeout functions of a #DBusConnection
525  * to integrate the connection with the GLib main loop.
526  * Pass in #NULL for the #GMainContext unless you're
527  * doing something specialized.
528  *
529  * If called twice for the same context, does nothing the second
530  * time. If called once with context A and once with context B,
531  * context B replaces context A as the context monitoring the
532  * connection.
533  */
534 void
535 atspi_dbus_connection_setup_with_g_main (DBusConnection *connection,
536                                    GMainContext   *context)
537 {
538   ConnectionSetup *old_setup;
539   ConnectionSetup *cs;
540   
541   /* FIXME we never free the slot, so its refcount just keeps growing,
542    * which is kind of broken.
543    */
544   dbus_connection_allocate_data_slot (&_dbus_gmain_connection_slot);
545   if (_dbus_gmain_connection_slot < 0)
546     goto nomem;
547
548   if (context == NULL)
549     context = g_main_context_default ();
550
551   cs = NULL;
552   
553   old_setup = dbus_connection_get_data (connection, _dbus_gmain_connection_slot);
554   if (old_setup != NULL)
555     {
556       if (old_setup->context == context)
557         return; /* nothing to do */
558
559       cs = connection_setup_new_from_old (context, old_setup);
560       
561       /* Nuke the old setup */
562       dbus_connection_set_data (connection, _dbus_gmain_connection_slot, NULL, NULL);
563       old_setup = NULL;
564     }
565
566   if (cs == NULL)
567     cs = connection_setup_new (context, connection);
568
569   if (!dbus_connection_set_data (connection, _dbus_gmain_connection_slot, cs,
570                                  (DBusFreeFunction)connection_setup_free))
571     goto nomem;
572   
573   if (!dbus_connection_set_watch_functions (connection,
574                                             add_watch,
575                                             remove_watch,
576                                             watch_toggled,
577                                             cs, NULL))
578     goto nomem;
579
580   if (!dbus_connection_set_timeout_functions (connection,
581                                               add_timeout,
582                                               remove_timeout,
583                                               timeout_toggled,
584                                               cs, NULL))
585     goto nomem;
586     
587   dbus_connection_set_wakeup_main_function (connection,
588                                             wakeup_main,
589                                             cs, NULL);
590       
591   return;
592
593  nomem:
594   g_error ("Not enough memory to set up DBusConnection for use with GLib");
595 }
596
597 /**
598  * atspi_dbus_server_setup_with_g_main:
599  * @server: the server
600  * @context: the #GMainContext or #NULL for default
601  *
602  * Sets the watch and timeout functions of a #DBusServer
603  * to integrate the server with the GLib main loop.
604  * In most cases the context argument should be #NULL.
605  *
606  * If called twice for the same context, does nothing the second
607  * time. If called once with context A and once with context B,
608  * context B replaces context A as the context monitoring the
609  * connection.
610  */
611 void
612 atspi_dbus_server_setup_with_g_main (DBusServer   *server,
613                                GMainContext *context)
614 {
615   ConnectionSetup *old_setup;
616   ConnectionSetup *cs;
617   
618   /* FIXME we never free the slot, so its refcount just keeps growing,
619    * which is kind of broken.
620    */
621   dbus_server_allocate_data_slot (&server_slot);
622   if (server_slot < 0)
623     goto nomem;
624
625   if (context == NULL)
626     context = g_main_context_default ();
627
628   cs = NULL;
629   
630   old_setup = dbus_server_get_data (server, server_slot);
631   if (old_setup != NULL)
632     {
633       if (old_setup->context == context)
634         return; /* nothing to do */
635
636       cs = connection_setup_new_from_old (context, old_setup);
637       
638       /* Nuke the old setup */
639       if (!dbus_server_set_data (server, server_slot, NULL, NULL))
640         goto nomem;
641       old_setup = NULL;
642     }
643
644   if (cs == NULL)
645     cs = connection_setup_new (context, NULL);
646
647   if (!dbus_server_set_data (server, server_slot, cs,
648                              (DBusFreeFunction)connection_setup_free))
649     goto nomem;
650   
651   if (!dbus_server_set_watch_functions (server,
652                                         add_watch,
653                                         remove_watch,
654                                         watch_toggled,
655                                         cs, NULL))
656     goto nomem;
657
658   if (!dbus_server_set_timeout_functions (server,
659                                           add_timeout,
660                                           remove_timeout,
661                                           timeout_toggled,
662                                           cs, NULL))
663     goto nomem;
664       
665   return;
666
667  nomem:
668   g_error ("Not enough memory to set up DBusServer for use with GLib");
669 }