2003-09-03 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / glib / dbus-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  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <config.h>
25 #include "dbus-glib.h"
26 #include "dbus-gtest.h"
27
28 /**
29  * @defgroup DBusGLib GLib bindings
30  * @ingroup  DBus
31  * @brief API for using D-BUS with GLib
32  *
33  * Convenience functions are provided for using D-BUS
34  * with the GLib library (see http://www.gtk.org for GLib
35  * information).
36  * 
37  */
38
39 /**
40  * @defgroup DBusGLibInternals GLib bindings implementation details
41  * @ingroup  DBusInternals
42  * @brief Implementation details of GLib bindings
43  *
44  * @{
45  */
46
47 /** @typedef DBusGSource
48  * A GSource representing a #DBusConnection or #DBusServer
49  */
50 typedef struct DBusGSource DBusGSource;
51
52 struct DBusGSource
53 {
54   GSource source; /**< the parent GSource */
55
56   GList *poll_fds;      /**< descriptors we're watching */
57   GHashTable *watches;  /**< hash of DBusWatch objects */
58
59   GMainContext *context; /**< the GMainContext to use, NULL for default */
60
61   void *connection_or_server; /**< DBusConnection or DBusServer */
62 };
63
64 static dbus_int32_t connection_slot = -1;
65 static dbus_int32_t server_slot = -1;
66
67 static gboolean gsource_connection_prepare  (GSource     *source,
68                                              gint        *timeout);
69 static gboolean gsource_connection_check    (GSource     *source);
70 static gboolean gsource_connection_dispatch (GSource     *source,
71                                              GSourceFunc  callback,
72                                              gpointer     user_data);
73 static gboolean gsource_server_prepare      (GSource     *source,
74                                              gint        *timeout);
75 static gboolean gsource_server_check        (GSource     *source);
76 static gboolean gsource_server_dispatch     (GSource     *source,
77                                              GSourceFunc  callback,
78                                              gpointer     user_data);
79
80 static GSourceFuncs dbus_connection_funcs = {
81   gsource_connection_prepare,
82   gsource_connection_check,
83   gsource_connection_dispatch,
84   NULL
85 };
86
87 static GSourceFuncs dbus_server_funcs = {
88   gsource_server_prepare,
89   gsource_server_check,
90   gsource_server_dispatch,
91   NULL
92 };
93
94 static gboolean
95 gsource_connection_prepare (GSource *source,
96                             gint    *timeout)
97 {
98   DBusConnection *connection = ((DBusGSource *)source)->connection_or_server;
99   
100   *timeout = -1;
101
102   return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS);  
103 }
104
105 static gboolean
106 gsource_server_prepare (GSource *source,
107                         gint    *timeout)
108 {
109   *timeout = -1;
110
111   return FALSE;
112 }
113
114 static gboolean
115 dbus_gsource_check (GSource *source)
116 {
117   DBusGSource *dbus_source = (DBusGSource *)source;
118   GList *list;
119
120   list = dbus_source->poll_fds;
121
122   while (list)
123     {
124       GPollFD *poll_fd = list->data;
125
126       if (poll_fd->revents != 0)
127         return TRUE;
128
129       list = list->next;
130     }
131
132   return FALSE;  
133 }
134
135 static gboolean
136 gsource_connection_check (GSource *source)
137 {
138   return dbus_gsource_check (source);
139 }
140
141 static gboolean
142 gsource_server_check (GSource *source)
143 {
144   return dbus_gsource_check (source);
145 }
146
147 static gboolean
148 dbus_gsource_dispatch (GSource     *source,
149                        GSourceFunc  callback,
150                        gpointer     user_data,
151                        dbus_bool_t  is_server)
152 {
153    DBusGSource *dbus_source = (DBusGSource *)source;
154    GList *copy, *list;
155
156    /* We need to traverse a copy of the list, since it can change in
157       dbus_watch_handle(). */
158    copy = g_list_copy (dbus_source->poll_fds);
159
160    list = copy;
161    while (list)
162      {
163        GPollFD *poll_fd = list->data;
164
165        if (poll_fd->revents != 0)
166          {
167            DBusWatch *watch = g_hash_table_lookup (dbus_source->watches, poll_fd);
168            guint condition = 0;
169            
170            if (poll_fd->revents & G_IO_IN)
171              condition |= DBUS_WATCH_READABLE;
172            if (poll_fd->revents & G_IO_OUT)
173              condition |= DBUS_WATCH_WRITABLE;
174            if (poll_fd->revents & G_IO_ERR)
175              condition |= DBUS_WATCH_ERROR;
176            if (poll_fd->revents & G_IO_HUP)
177              condition |= DBUS_WATCH_HANGUP;
178
179            dbus_watch_handle (watch, condition);
180          }
181
182        list = list->next;
183      }
184
185    g_list_free (copy);   
186
187    return TRUE;
188 }
189
190 static gboolean
191 gsource_connection_dispatch (GSource     *source,
192                              GSourceFunc  callback,
193                              gpointer     user_data)
194 {
195   DBusGSource *dbus_source = (DBusGSource *)source;
196   DBusConnection *connection = dbus_source->connection_or_server;
197
198   dbus_connection_ref (connection);
199
200   dbus_gsource_dispatch (source, callback, user_data,
201                          FALSE);
202   
203   /* Dispatch messages */
204   while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS)
205     ;
206
207    dbus_connection_unref (connection);
208    
209    return TRUE;
210 }
211
212 static gboolean
213 gsource_server_dispatch (GSource     *source,
214                          GSourceFunc  callback,
215                          gpointer     user_data)
216 {
217   DBusGSource *dbus_source = (DBusGSource *)source;
218   DBusServer *server = dbus_source->connection_or_server;
219
220   dbus_server_ref (server);
221
222   dbus_gsource_dispatch (source, callback, user_data,
223                          TRUE);
224
225   dbus_server_unref (server);
226    
227   return TRUE;
228 }
229      
230 static dbus_bool_t
231 add_watch (DBusWatch *watch,
232            gpointer   data)
233 {
234   GPollFD *poll_fd;
235   DBusGSource *dbus_source;
236   guint flags;
237
238   if (!dbus_watch_get_enabled (watch))
239     return TRUE;
240   
241   dbus_source = data;
242   
243   poll_fd = g_new (GPollFD, 1);
244   poll_fd->fd = dbus_watch_get_fd (watch);
245   poll_fd->events = 0;
246   flags = dbus_watch_get_flags (watch);
247   dbus_watch_set_data (watch, poll_fd, NULL);
248
249   if (flags & DBUS_WATCH_READABLE)
250     poll_fd->events |= G_IO_IN;
251   if (flags & DBUS_WATCH_WRITABLE)
252     poll_fd->events |= G_IO_OUT;
253   poll_fd->events |= G_IO_ERR | G_IO_HUP;
254
255   g_source_add_poll ((GSource *)dbus_source, poll_fd);
256
257   dbus_source->poll_fds = g_list_prepend (dbus_source->poll_fds, poll_fd);
258   g_hash_table_insert (dbus_source->watches, poll_fd, watch);
259
260   return TRUE;
261 }
262
263 static void
264 remove_watch (DBusWatch *watch,
265               gpointer   data)
266 {
267   DBusGSource *dbus_source = data;
268   GPollFD *poll_fd;
269   
270   poll_fd = dbus_watch_get_data (watch);
271   if (poll_fd == NULL)
272     return; /* probably a not-enabled watch that was added */
273   
274   dbus_source->poll_fds = g_list_remove (dbus_source->poll_fds, poll_fd);
275   g_hash_table_remove (dbus_source->watches, poll_fd);
276   g_source_remove_poll ((GSource *)dbus_source, poll_fd);
277
278   dbus_watch_set_data (watch, NULL, NULL); /* needed due to watch_toggled
279                                             * breaking add/remove symmetry
280                                             */
281   
282   g_free (poll_fd);
283 }
284
285 static void
286 watch_toggled (DBusWatch *watch,
287                void      *data)
288 {
289   /* Because we just exit on OOM, enable/disable is
290    * no different from add/remove
291    */
292   if (dbus_watch_get_enabled (watch))
293     add_watch (watch, data);
294   else
295     remove_watch (watch, data);
296 }
297
298 static gboolean
299 timeout_handler (gpointer data)
300 {
301   DBusTimeout *timeout = data;
302
303   dbus_timeout_handle (timeout);
304
305   return TRUE;
306 }
307
308 static dbus_bool_t
309 add_timeout (DBusTimeout *timeout,
310              void        *data)
311 {
312   DBusGSource *dbus_source = data;
313   GSource *source;
314
315   if (!dbus_timeout_get_enabled (timeout))
316     return TRUE;
317   
318   source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
319   g_source_set_callback (source, timeout_handler, timeout, NULL);
320   g_source_attach (source, dbus_source->context);
321   
322   dbus_timeout_set_data (timeout, GUINT_TO_POINTER (g_source_get_id (source)),
323                          NULL);
324
325   return TRUE;
326 }
327
328 static void
329 remove_timeout (DBusTimeout *timeout,
330                 void        *data)
331 {
332   guint timeout_tag;
333   
334   timeout_tag = GPOINTER_TO_UINT (dbus_timeout_get_data (timeout));
335
336   if (timeout_tag != 0) /* if 0, probably timeout was disabled */
337     g_source_remove (timeout_tag);
338 }
339
340 static void
341 timeout_toggled (DBusTimeout *timeout,
342                  void        *data)
343 {
344   /* Because we just exit on OOM, enable/disable is
345    * no different from add/remove
346    */
347   if (dbus_timeout_get_enabled (timeout))
348     add_timeout (timeout, data);
349   else
350     remove_timeout (timeout, data);
351 }
352
353
354 static void
355 free_source (GSource *source)
356 {
357   g_source_destroy (source);
358 }
359
360 static void
361 wakeup_main (void *data)
362 {
363   DBusGSource *dbus_source = data;
364
365   g_main_context_wakeup (dbus_source->context);
366 }
367
368
369 /** @} */ /* End of GLib bindings internals */
370
371 /** @addtogroup DBusGLib
372  * @{
373  */
374
375 static GSource*
376 create_source (void         *connection_or_server,
377                GSourceFuncs *funcs,
378                GMainContext *context)
379 {
380   GSource *source;
381   DBusGSource *dbus_source;
382
383   source = g_source_new (funcs, sizeof (DBusGSource));
384   
385   dbus_source = (DBusGSource *)source;  
386   dbus_source->watches = g_hash_table_new (NULL, NULL);
387   dbus_source->connection_or_server = connection_or_server;
388   dbus_source->context = context;
389
390   return source;
391 }
392
393 /**
394  * Sets the watch and timeout functions of a #DBusConnection
395  * to integrate the connection with the GLib main loop.
396  * Pass in #NULL for the #GMainContext unless you're
397  * doing something specialized.
398  *
399  * @param connection the connection
400  * @param context the #GMainContext or #NULL for default context
401  */
402 void
403 dbus_connection_setup_with_g_main (DBusConnection *connection,
404                                    GMainContext   *context)
405 {
406   GSource *source;
407
408   source = create_source (connection, &dbus_connection_funcs, context);
409
410   if (!dbus_connection_set_watch_functions (connection,
411                                             add_watch,
412                                             remove_watch,
413                                             watch_toggled,
414                                             source, NULL))
415     goto nomem;
416
417   if (!dbus_connection_set_timeout_functions (connection,
418                                               add_timeout,
419                                               remove_timeout,
420                                               timeout_toggled,
421                                               NULL, NULL))
422     goto nomem;
423     
424   dbus_connection_set_wakeup_main_function (connection,
425                                             wakeup_main,
426                                             source, NULL);
427       
428   g_source_attach (source, context);
429
430   /* FIXME we never free the slot, so its refcount just keeps growing,
431    * which is kind of broken.
432    */
433   dbus_connection_allocate_data_slot (&connection_slot);
434   if (connection_slot < 0)
435     goto nomem;
436
437   if (!dbus_connection_set_data (connection, connection_slot, source,
438                                  (DBusFreeFunction)free_source))
439     goto nomem;
440
441   return;
442
443  nomem:
444   g_error ("Not enough memory to set up DBusConnection for use with GLib");
445 }
446
447 /**
448  * Sets the watch and timeout functions of a #DBusServer
449  * to integrate the server with the GLib main loop.
450  * In most cases the context argument should be #NULL.
451  *
452  * @param server the server
453  * @param context the #GMainContext or #NULL for default
454  */
455 void
456 dbus_server_setup_with_g_main (DBusServer   *server,
457                                GMainContext *context)
458 {
459   GSource *source;
460
461   source = create_source (server, &dbus_server_funcs, context);
462
463   dbus_server_set_watch_functions (server,
464                                    add_watch,
465                                    remove_watch,
466                                    watch_toggled,
467                                    source, NULL);
468
469   dbus_server_set_timeout_functions (server,
470                                      add_timeout,
471                                      remove_timeout,
472                                      timeout_toggled,
473                                      NULL, NULL);
474   
475   g_source_attach (source, context);
476
477   /* FIXME we never free the slot, so its refcount just keeps growing,
478    * which is kind of broken.
479    */
480   dbus_server_allocate_data_slot (&server_slot);
481   if (server_slot < 0)
482     goto nomem;
483
484   if (!dbus_server_set_data (server, server_slot, source,
485                              (DBusFreeFunction)free_source))
486     goto nomem;
487
488   return;
489
490  nomem:
491   g_error ("Not enough memory to set up DBusServer for use with GLib");
492 }
493
494 /** @} */ /* end of public API */
495
496 #ifdef DBUS_BUILD_TESTS
497
498 /**
499  * @ingroup DBusGLibInternals
500  * Unit test for GLib main loop integration
501  * @returns #TRUE on success.
502  */
503 dbus_bool_t
504 _dbus_gmain_test (const char *test_data_dir)
505 {
506   
507   return TRUE;
508 }
509
510 #endif /* DBUS_BUILD_TESTS */