0e7b143c82a8cf718618524533e9168af624dff7
[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.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
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
143       g_assert (cs->message_queue_source != NULL);
144
145       ((DBusGMessageQueue*)cs->message_queue_source)->connection = connection;
146       g_source_attach (cs->message_queue_source, cs->context);
147     }
148   
149   return cs;
150 }
151
152 static void
153 io_handler_source_finalized (gpointer data)
154 {
155   IOHandler *handler;
156
157   handler = data;
158
159   if (handler->watch)
160     dbus_watch_set_data (handler->watch, NULL, NULL);
161   
162   g_free (handler);
163 }
164
165 static void
166 io_handler_destroy_source (void *data)
167 {
168   IOHandler *handler;
169
170   handler = data;
171
172   if (handler->source)
173     {
174       GSource *source = handler->source;
175       handler->source = NULL;
176       handler->cs->ios = g_slist_remove (handler->cs->ios, handler);
177       g_source_destroy (source);
178       g_source_unref (source);
179     }
180 }
181
182 static void
183 io_handler_watch_freed (void *data)
184 {
185   IOHandler *handler;
186
187   handler = data;
188
189   handler->watch = NULL;
190
191   io_handler_destroy_source (handler);
192 }
193
194 static gboolean
195 io_handler_dispatch (GIOChannel   *source,
196                      GIOCondition  condition,
197                      gpointer      data)
198 {
199   IOHandler *handler;
200   guint dbus_condition = 0;
201   DBusConnection *connection;
202
203   handler = data;
204
205   connection = handler->cs->connection;
206   
207   if (connection)
208     dbus_connection_ref (connection);
209   
210   if (condition & G_IO_IN)
211     dbus_condition |= DBUS_WATCH_READABLE;
212   if (condition & G_IO_OUT)
213     dbus_condition |= DBUS_WATCH_WRITABLE;
214   if (condition & G_IO_ERR)
215     dbus_condition |= DBUS_WATCH_ERROR;
216   if (condition & G_IO_HUP)
217     dbus_condition |= DBUS_WATCH_HANGUP;
218
219   /* Note that we don't touch the handler after this, because
220    * dbus may have disabled the watch and thus killed the
221    * handler.
222    */
223   dbus_watch_handle (handler->watch, dbus_condition);
224   handler = NULL;
225
226   if (connection)
227     dbus_connection_unref (connection);
228   
229   return TRUE;
230 }
231
232 /* Attach the connection setup to the given watch, removing any
233  * previously-attached connection setup.
234  */
235 static void
236 connection_setup_add_watch (ConnectionSetup *cs,
237                             DBusWatch       *watch)
238 {
239   guint flags;
240   GIOCondition condition;
241   GIOChannel *channel;
242   IOHandler *handler;
243   
244   if (!dbus_watch_get_enabled (watch))
245     return;
246   
247   flags = dbus_watch_get_flags (watch);
248
249   condition = G_IO_ERR | G_IO_HUP;
250   if (flags & DBUS_WATCH_READABLE)
251     condition |= G_IO_IN;
252   if (flags & DBUS_WATCH_WRITABLE)
253     condition |= G_IO_OUT;
254
255   handler = g_new0 (IOHandler, 1);
256   handler->cs = cs;
257   handler->watch = watch;
258   
259   channel = g_io_channel_unix_new (dbus_watch_get_unix_fd (watch));
260   
261   handler->source = g_io_create_watch (channel, condition);
262   g_source_set_callback (handler->source, (GSourceFunc) io_handler_dispatch, handler,
263                          io_handler_source_finalized);
264   g_source_attach (handler->source, cs->context);
265
266   cs->ios = g_slist_prepend (cs->ios, handler);
267   
268   dbus_watch_set_data (watch, handler, io_handler_watch_freed);
269   g_io_channel_unref (channel);
270 }
271
272 static void
273 connection_setup_remove_watch (ConnectionSetup *cs,
274                                DBusWatch       *watch)
275 {
276   IOHandler *handler;
277
278   handler = dbus_watch_get_data (watch);
279
280   if (handler == NULL || handler->cs != cs)
281     return;
282   
283   io_handler_destroy_source (handler);
284 }
285
286 static void
287 timeout_handler_source_finalized (gpointer data)
288 {
289   TimeoutHandler *handler;
290
291   handler = data;
292
293   if (handler->timeout)
294     dbus_timeout_set_data (handler->timeout, NULL, NULL);
295   
296   g_free (handler);
297 }
298
299 static void
300 timeout_handler_destroy_source (void *data)
301 {
302   TimeoutHandler *handler;
303
304   handler = data;
305
306   if (handler->source)
307     {
308       GSource *source = handler->source;
309       handler->source = NULL;
310       handler->cs->timeouts = g_slist_remove (handler->cs->timeouts, handler);
311       g_source_destroy (source);
312       g_source_unref (source);
313     }
314 }
315
316 static void
317 timeout_handler_timeout_freed (void *data)
318 {
319   TimeoutHandler *handler;
320
321   handler = data;
322
323   handler->timeout = NULL;
324
325   timeout_handler_destroy_source (handler);
326 }
327
328 static gboolean
329 timeout_handler_dispatch (gpointer      data)
330 {
331   TimeoutHandler *handler;
332
333   handler = data;
334
335   dbus_timeout_handle (handler->timeout);
336   
337   return TRUE;
338 }
339
340 static void
341 connection_setup_add_timeout (ConnectionSetup *cs,
342                               DBusTimeout     *timeout)
343 {
344   TimeoutHandler *handler;
345   
346   if (!dbus_timeout_get_enabled (timeout))
347     return;
348   
349   handler = g_new0 (TimeoutHandler, 1);
350   handler->cs = cs;
351   handler->timeout = timeout;
352
353   handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
354   g_source_set_callback (handler->source, timeout_handler_dispatch, handler,
355                          timeout_handler_source_finalized);
356   g_source_attach (handler->source, handler->cs->context);
357
358   cs->timeouts = g_slist_prepend (cs->timeouts, handler);
359
360   dbus_timeout_set_data (timeout, handler, timeout_handler_timeout_freed);
361 }
362
363 static void
364 connection_setup_remove_timeout (ConnectionSetup *cs,
365                                  DBusTimeout       *timeout)
366 {
367   TimeoutHandler *handler;
368   
369   handler = dbus_timeout_get_data (timeout);
370
371   if (handler == NULL)
372     return;
373   
374   timeout_handler_destroy_source (handler);
375 }
376
377 static void
378 connection_setup_free (ConnectionSetup *cs)
379 {
380   while (cs->ios)
381     io_handler_destroy_source (cs->ios->data);
382
383   while (cs->timeouts)
384     timeout_handler_destroy_source (cs->timeouts->data);
385
386   if (cs->message_queue_source)
387     {
388       GSource *source;
389
390       source = cs->message_queue_source;
391       cs->message_queue_source = NULL;
392
393       g_source_destroy (source);
394       g_source_unref (source);
395     }
396   
397   g_main_context_unref (cs->context);
398   g_free (cs);
399 }
400
401 static dbus_bool_t
402 add_watch (DBusWatch *watch,
403            gpointer   data)
404 {
405   ConnectionSetup *cs;
406
407   cs = data;
408
409   connection_setup_add_watch (cs, watch);
410   
411   return TRUE;
412 }
413
414 static void
415 remove_watch (DBusWatch *watch,
416               gpointer   data)
417 {
418   ConnectionSetup *cs;
419
420   cs = data;
421
422   connection_setup_remove_watch (cs, watch);
423 }
424
425 static void
426 watch_toggled (DBusWatch *watch,
427                void      *data)
428 {
429   /* Because we just exit on OOM, enable/disable is
430    * no different from add/remove
431    */
432   if (dbus_watch_get_enabled (watch))
433     add_watch (watch, data);
434   else
435     remove_watch (watch, data);
436 }
437
438 static dbus_bool_t
439 add_timeout (DBusTimeout *timeout,
440              void        *data)
441 {
442   ConnectionSetup *cs;
443
444   cs = data;
445   
446   if (!dbus_timeout_get_enabled (timeout))
447     return TRUE;
448
449   connection_setup_add_timeout (cs, timeout);
450
451   return TRUE;
452 }
453
454 static void
455 remove_timeout (DBusTimeout *timeout,
456                 void        *data)
457 {
458   ConnectionSetup *cs;
459
460   cs = data;
461
462   connection_setup_remove_timeout (cs, timeout);
463 }
464
465 static void
466 timeout_toggled (DBusTimeout *timeout,
467                  void        *data)
468 {
469   /* Because we just exit on OOM, enable/disable is
470    * no different from add/remove
471    */
472   if (dbus_timeout_get_enabled (timeout))
473     add_timeout (timeout, data);
474   else
475     remove_timeout (timeout, data);
476 }
477
478 static void
479 wakeup_main (void *data)
480 {
481   ConnectionSetup *cs = data;
482
483   g_main_context_wakeup (cs->context);
484 }
485
486
487 /* Move to a new context */
488 static ConnectionSetup*
489 connection_setup_new_from_old (GMainContext    *context,
490                                ConnectionSetup *old)
491 {
492   ConnectionSetup *cs;
493
494   g_assert (old->context != context);
495   
496   cs = connection_setup_new (context, old->connection);
497   
498   while (old->ios != NULL)
499     {
500       IOHandler *handler = old->ios->data;
501
502       connection_setup_add_watch (cs, handler->watch);
503       /* The old handler will be removed from old->ios as a side-effect */
504     }
505
506   while (old->timeouts != NULL)
507     {
508       TimeoutHandler *handler = old->timeouts->data;
509
510       connection_setup_add_timeout (cs, handler->timeout);
511     }
512
513   return cs;
514 }
515
516 /* @} */ /* End of GLib bindings internals */
517
518 /**
519  * atspi_dbus_connection_setup_with_g_main: (skip)
520  * @connection: the connection
521  * @context: the #GMainContext or #NULL for default context
522  *
523  * Sets the watch and timeout functions of a #DBusConnection
524  * to integrate the connection with the GLib main loop.
525  * Pass in #NULL for the #GMainContext unless you're
526  * doing something specialized.
527  *
528  * If called twice for the same context, does nothing the second
529  * time. If called once with context A and once with context B,
530  * context B replaces context A as the context monitoring the
531  * connection.
532  */
533 void
534 atspi_dbus_connection_setup_with_g_main (DBusConnection *connection,
535                                    GMainContext   *context)
536 {
537   ConnectionSetup *old_setup;
538   ConnectionSetup *cs;
539   
540   /* FIXME we never free the slot, so its refcount just keeps growing,
541    * which is kind of broken.
542    */
543   dbus_connection_allocate_data_slot (&_dbus_gmain_connection_slot);
544   if (_dbus_gmain_connection_slot < 0)
545     goto nomem;
546
547   if (context == NULL)
548     context = g_main_context_default ();
549
550   cs = NULL;
551   
552   old_setup = dbus_connection_get_data (connection, _dbus_gmain_connection_slot);
553   if (old_setup != NULL)
554     {
555       if (old_setup->context == context)
556         return; /* nothing to do */
557
558       cs = connection_setup_new_from_old (context, old_setup);
559       
560       /* Nuke the old setup */
561       dbus_connection_set_data (connection, _dbus_gmain_connection_slot, NULL, NULL);
562       old_setup = NULL;
563     }
564
565   if (cs == NULL)
566     cs = connection_setup_new (context, connection);
567
568   if (!dbus_connection_set_data (connection, _dbus_gmain_connection_slot, cs,
569                                  (DBusFreeFunction)connection_setup_free))
570     goto nomem;
571   
572   if (!dbus_connection_set_watch_functions (connection,
573                                             add_watch,
574                                             remove_watch,
575                                             watch_toggled,
576                                             cs, NULL))
577     goto nomem;
578
579   if (!dbus_connection_set_timeout_functions (connection,
580                                               add_timeout,
581                                               remove_timeout,
582                                               timeout_toggled,
583                                               cs, NULL))
584     goto nomem;
585     
586   dbus_connection_set_wakeup_main_function (connection,
587                                             wakeup_main,
588                                             cs, NULL);
589       
590   return;
591
592  nomem:
593   g_error ("Not enough memory to set up DBusConnection for use with GLib");
594 }
595
596 /**
597  * atspi_dbus_server_setup_with_g_main: (skip)
598  * @server: the server
599  * @context: the #GMainContext or #NULL for default
600  *
601  * Sets the watch and timeout functions of a #DBusServer
602  * to integrate the server with the GLib main loop.
603  * In most cases the context argument should be #NULL.
604  *
605  * If called twice for the same context, does nothing the second
606  * time. If called once with context A and once with context B,
607  * context B replaces context A as the context monitoring the
608  * connection.
609  */
610 void
611 atspi_dbus_server_setup_with_g_main (DBusServer   *server,
612                                GMainContext *context)
613 {
614   ConnectionSetup *old_setup;
615   ConnectionSetup *cs;
616   
617   /* FIXME we never free the slot, so its refcount just keeps growing,
618    * which is kind of broken.
619    */
620   dbus_server_allocate_data_slot (&server_slot);
621   if (server_slot < 0)
622     goto nomem;
623
624   if (context == NULL)
625     context = g_main_context_default ();
626
627   cs = NULL;
628   
629   old_setup = dbus_server_get_data (server, server_slot);
630   if (old_setup != NULL)
631     {
632       if (old_setup->context == context)
633         return; /* nothing to do */
634
635       cs = connection_setup_new_from_old (context, old_setup);
636       
637       /* Nuke the old setup */
638       if (!dbus_server_set_data (server, server_slot, NULL, NULL))
639         goto nomem;
640       old_setup = NULL;
641     }
642
643   if (cs == NULL)
644     cs = connection_setup_new (context, NULL);
645
646   if (!dbus_server_set_data (server, server_slot, cs,
647                              (DBusFreeFunction)connection_setup_free))
648     goto nomem;
649   
650   if (!dbus_server_set_watch_functions (server,
651                                         add_watch,
652                                         remove_watch,
653                                         watch_toggled,
654                                         cs, NULL))
655     goto nomem;
656
657   if (!dbus_server_set_timeout_functions (server,
658                                           add_timeout,
659                                           remove_timeout,
660                                           timeout_toggled,
661                                           cs, NULL))
662     goto nomem;
663       
664   return;
665
666  nomem:
667   g_error ("Not enough memory to set up DBusServer for use with GLib");
668 }