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