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