2.34.0
[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       ((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 /**
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
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
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 }