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