BGO#657051: Fix build error on Solaris
[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   g_assert (dbus_timeout_get_data (timeout) == NULL);
355
356   handler = g_new0 (TimeoutHandler, 1);
357   handler->cs = cs;
358   handler->timeout = timeout;
359
360   handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
361   g_source_set_callback (handler->source, timeout_handler_dispatch, handler,
362                          timeout_handler_source_finalized);
363   g_source_attach (handler->source, handler->cs->context);
364
365   cs->timeouts = g_slist_prepend (cs->timeouts, handler);
366
367   dbus_timeout_set_data (timeout, handler, timeout_handler_timeout_freed);
368 }
369
370 static void
371 connection_setup_remove_timeout (ConnectionSetup *cs,
372                                  DBusTimeout       *timeout)
373 {
374   TimeoutHandler *handler;
375   
376   handler = dbus_timeout_get_data (timeout);
377
378   if (handler == NULL)
379     return;
380   
381   timeout_handler_destroy_source (handler);
382 }
383
384 static void
385 connection_setup_free (ConnectionSetup *cs)
386 {
387   while (cs->ios)
388     io_handler_destroy_source (cs->ios->data);
389
390   while (cs->timeouts)
391     timeout_handler_destroy_source (cs->timeouts->data);
392
393   if (cs->message_queue_source)
394     {
395       GSource *source;
396
397       source = cs->message_queue_source;
398       cs->message_queue_source = NULL;
399
400       g_source_destroy (source);
401       g_source_unref (source);
402     }
403   
404   g_main_context_unref (cs->context);
405   g_free (cs);
406 }
407
408 static dbus_bool_t
409 add_watch (DBusWatch *watch,
410            gpointer   data)
411 {
412   ConnectionSetup *cs;
413
414   cs = data;
415
416   connection_setup_add_watch (cs, watch);
417   
418   return TRUE;
419 }
420
421 static void
422 remove_watch (DBusWatch *watch,
423               gpointer   data)
424 {
425   ConnectionSetup *cs;
426
427   cs = data;
428
429   connection_setup_remove_watch (cs, watch);
430 }
431
432 static void
433 watch_toggled (DBusWatch *watch,
434                void      *data)
435 {
436   /* Because we just exit on OOM, enable/disable is
437    * no different from add/remove
438    */
439   if (dbus_watch_get_enabled (watch))
440     add_watch (watch, data);
441   else
442     remove_watch (watch, data);
443 }
444
445 static dbus_bool_t
446 add_timeout (DBusTimeout *timeout,
447              void        *data)
448 {
449   ConnectionSetup *cs;
450
451   cs = data;
452   
453   if (!dbus_timeout_get_enabled (timeout))
454     return TRUE;
455
456   connection_setup_add_timeout (cs, timeout);
457
458   return TRUE;
459 }
460
461 static void
462 remove_timeout (DBusTimeout *timeout,
463                 void        *data)
464 {
465   ConnectionSetup *cs;
466
467   cs = data;
468
469   connection_setup_remove_timeout (cs, timeout);
470 }
471
472 static void
473 timeout_toggled (DBusTimeout *timeout,
474                  void        *data)
475 {
476   /* Because we just exit on OOM, enable/disable is
477    * no different from add/remove
478    */
479   if (dbus_timeout_get_enabled (timeout))
480     add_timeout (timeout, data);
481   else
482     remove_timeout (timeout, data);
483 }
484
485 static void
486 wakeup_main (void *data)
487 {
488   ConnectionSetup *cs = data;
489
490   g_main_context_wakeup (cs->context);
491 }
492
493
494 /* Move to a new context */
495 static ConnectionSetup*
496 connection_setup_new_from_old (GMainContext    *context,
497                                ConnectionSetup *old)
498 {
499   ConnectionSetup *cs;
500
501   g_assert (old->context != context);
502   
503   cs = connection_setup_new (context, old->connection);
504   
505   while (old->ios != NULL)
506     {
507       IOHandler *handler = old->ios->data;
508
509       connection_setup_add_watch (cs, handler->watch);
510       /* The old handler will be removed from old->ios as a side-effect */
511     }
512
513   while (old->timeouts != NULL)
514     {
515       TimeoutHandler *handler = old->timeouts->data;
516
517       connection_setup_add_timeout (cs, handler->timeout);
518     }
519
520   return cs;
521 }
522
523 /** @} */ /* End of GLib bindings internals */
524
525 /** @addtogroup DBusGLib
526  * @{
527  */
528
529 /**
530  * atspi_dbus_connection_setup_with_g_main:
531  * @connection: the connection
532  * @context: the #GMainContext or #NULL for default context
533  *
534  * Sets the watch and timeout functions of a #DBusConnection
535  * to integrate the connection with the GLib main loop.
536  * Pass in #NULL for the #GMainContext unless you're
537  * doing something specialized.
538  *
539  * If called twice for the same context, does nothing the second
540  * time. If called once with context A and once with context B,
541  * context B replaces context A as the context monitoring the
542  * connection.
543  */
544 void
545 atspi_dbus_connection_setup_with_g_main (DBusConnection *connection,
546                                    GMainContext   *context)
547 {
548   ConnectionSetup *old_setup;
549   ConnectionSetup *cs;
550   
551   /* FIXME we never free the slot, so its refcount just keeps growing,
552    * which is kind of broken.
553    */
554   dbus_connection_allocate_data_slot (&_dbus_gmain_connection_slot);
555   if (_dbus_gmain_connection_slot < 0)
556     goto nomem;
557
558   if (context == NULL)
559     context = g_main_context_default ();
560
561   cs = NULL;
562   
563   old_setup = dbus_connection_get_data (connection, _dbus_gmain_connection_slot);
564   if (old_setup != NULL)
565     {
566       if (old_setup->context == context)
567         return; /* nothing to do */
568
569       cs = connection_setup_new_from_old (context, old_setup);
570       
571       /* Nuke the old setup */
572       dbus_connection_set_data (connection, _dbus_gmain_connection_slot, NULL, NULL);
573       old_setup = NULL;
574     }
575
576   if (cs == NULL)
577     cs = connection_setup_new (context, connection);
578
579   if (!dbus_connection_set_data (connection, _dbus_gmain_connection_slot, cs,
580                                  (DBusFreeFunction)connection_setup_free))
581     goto nomem;
582   
583   if (!dbus_connection_set_watch_functions (connection,
584                                             add_watch,
585                                             remove_watch,
586                                             watch_toggled,
587                                             cs, NULL))
588     goto nomem;
589
590   if (!dbus_connection_set_timeout_functions (connection,
591                                               add_timeout,
592                                               remove_timeout,
593                                               timeout_toggled,
594                                               cs, NULL))
595     goto nomem;
596     
597   dbus_connection_set_wakeup_main_function (connection,
598                                             wakeup_main,
599                                             cs, NULL);
600       
601   return;
602
603  nomem:
604   g_error ("Not enough memory to set up DBusConnection for use with GLib");
605 }
606
607 /**
608  * atspi_dbus_server_setup_with_g_main:
609  * @server: the server
610  * @context: the #GMainContext or #NULL for default
611  *
612  * Sets the watch and timeout functions of a #DBusServer
613  * to integrate the server with the GLib main loop.
614  * In most cases the context argument should be #NULL.
615  *
616  * If called twice for the same context, does nothing the second
617  * time. If called once with context A and once with context B,
618  * context B replaces context A as the context monitoring the
619  * connection.
620  */
621 void
622 atspi_dbus_server_setup_with_g_main (DBusServer   *server,
623                                GMainContext *context)
624 {
625   ConnectionSetup *old_setup;
626   ConnectionSetup *cs;
627   
628   /* FIXME we never free the slot, so its refcount just keeps growing,
629    * which is kind of broken.
630    */
631   dbus_server_allocate_data_slot (&server_slot);
632   if (server_slot < 0)
633     goto nomem;
634
635   if (context == NULL)
636     context = g_main_context_default ();
637
638   cs = NULL;
639   
640   old_setup = dbus_server_get_data (server, server_slot);
641   if (old_setup != NULL)
642     {
643       if (old_setup->context == context)
644         return; /* nothing to do */
645
646       cs = connection_setup_new_from_old (context, old_setup);
647       
648       /* Nuke the old setup */
649       if (!dbus_server_set_data (server, server_slot, NULL, NULL))
650         goto nomem;
651       old_setup = NULL;
652     }
653
654   if (cs == NULL)
655     cs = connection_setup_new (context, NULL);
656
657   if (!dbus_server_set_data (server, server_slot, cs,
658                              (DBusFreeFunction)connection_setup_free))
659     goto nomem;
660   
661   if (!dbus_server_set_watch_functions (server,
662                                         add_watch,
663                                         remove_watch,
664                                         watch_toggled,
665                                         cs, NULL))
666     goto nomem;
667
668   if (!dbus_server_set_timeout_functions (server,
669                                           add_timeout,
670                                           remove_timeout,
671                                           timeout_toggled,
672                                           cs, NULL))
673     goto nomem;
674       
675   return;
676
677  nomem:
678   g_error ("Not enough memory to set up DBusServer for use with GLib");
679 }