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