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