5eb75d10002503d0116cfc60bdb6f74eb7d088a6
[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   void *connection_or_server; /**< DBusConnection or DBusServer */
59 };
60
61 static GStaticMutex connection_slot_lock = G_STATIC_MUTEX_INIT;
62 static int connection_slot = -1;
63 static GStaticMutex server_slot_lock = G_STATIC_MUTEX_INIT;
64 static int server_slot = -1;
65
66 static gboolean dbus_connection_prepare  (GSource     *source,
67                                           gint        *timeout);
68 static gboolean dbus_connection_check    (GSource     *source);
69 static gboolean dbus_connection_dispatch (GSource     *source,
70                                           GSourceFunc  callback,
71                                           gpointer     user_data);
72 static gboolean dbus_server_prepare      (GSource     *source,
73                                           gint        *timeout);
74 static gboolean dbus_server_check        (GSource     *source);
75 static gboolean dbus_server_dispatch     (GSource     *source,
76                                           GSourceFunc  callback,
77                                           gpointer     user_data);
78
79 static GSourceFuncs dbus_connection_funcs = {
80   dbus_connection_prepare,
81   dbus_connection_check,
82   dbus_connection_dispatch,
83   NULL
84 };
85
86 static GSourceFuncs dbus_server_funcs = {
87   dbus_server_prepare,
88   dbus_server_check,
89   dbus_server_dispatch,
90   NULL
91 };
92
93 static gboolean
94 dbus_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_n_messages (connection) > 0);  
102 }
103
104 static gboolean
105 dbus_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 dbus_connection_check (GSource *source)
136 {
137   return dbus_gsource_check (source);
138 }
139
140 static gboolean
141 dbus_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_connect_handle_watch. */
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            if (is_server)
179              dbus_server_handle_watch (dbus_source->connection_or_server,
180                                        watch, condition);
181            else
182              dbus_connection_handle_watch (dbus_source->connection_or_server,
183                                            watch, condition);
184          }
185
186        list = list->next;
187      }
188
189    g_list_free (copy);   
190
191    return TRUE;
192 }
193
194 static gboolean
195 dbus_connection_dispatch (GSource     *source,
196                           GSourceFunc  callback,
197                           gpointer     user_data)
198 {
199   DBusGSource *dbus_source = (DBusGSource *)source;
200   DBusConnection *connection = dbus_source->connection_or_server;
201
202   dbus_connection_ref (connection);
203
204   dbus_gsource_dispatch (source, callback, user_data,
205                          FALSE);
206   
207   /* Dispatch messages */
208   while (dbus_connection_dispatch_message (connection))
209     ;
210
211    dbus_connection_unref (connection);
212    
213    return TRUE;
214 }
215
216 static gboolean
217 dbus_server_dispatch (GSource     *source,
218                       GSourceFunc  callback,
219                       gpointer     user_data)
220 {
221   DBusGSource *dbus_source = (DBusGSource *)source;
222   DBusServer *server = dbus_source->connection_or_server;
223
224   dbus_server_ref (server);
225
226   dbus_gsource_dispatch (source, callback, user_data,
227                          TRUE);
228
229   dbus_server_unref (server);
230    
231   return TRUE;
232 }
233      
234 static dbus_bool_t
235 add_watch (DBusWatch *watch,
236            gpointer   data)
237 {
238   GPollFD *poll_fd;
239   DBusGSource *dbus_source;
240   guint flags;
241
242   if (!dbus_watch_get_enabled (watch))
243     return TRUE;
244   
245   dbus_source = data;
246   
247   poll_fd = g_new (GPollFD, 1);
248   poll_fd->fd = dbus_watch_get_fd (watch);
249   poll_fd->events = 0;
250   flags = dbus_watch_get_flags (watch);
251   dbus_watch_set_data (watch, poll_fd, NULL);
252
253   if (flags & DBUS_WATCH_READABLE)
254     poll_fd->events |= G_IO_IN;
255   if (flags & DBUS_WATCH_WRITABLE)
256     poll_fd->events |= G_IO_OUT;
257   poll_fd->events |= G_IO_ERR | G_IO_HUP;
258
259   g_source_add_poll ((GSource *)dbus_source, poll_fd);
260
261   dbus_source->poll_fds = g_list_prepend (dbus_source->poll_fds, poll_fd);
262   g_hash_table_insert (dbus_source->watches, poll_fd, watch);
263
264   return TRUE;
265 }
266
267 static void
268 remove_watch (DBusWatch *watch,
269               gpointer   data)
270 {
271   DBusGSource *dbus_source = data;
272   GPollFD *poll_fd;
273   
274   poll_fd = dbus_watch_get_data (watch);
275   if (poll_fd == NULL)
276     return; /* probably a not-enabled watch that was added */
277   
278   dbus_source->poll_fds = g_list_remove (dbus_source->poll_fds, poll_fd);
279   g_hash_table_remove (dbus_source->watches, poll_fd);
280   g_source_remove_poll ((GSource *)dbus_source, poll_fd);
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   guint timeout_tag;
313
314   if (!dbus_timeout_get_enabled (timeout))
315     return TRUE;
316   
317   timeout_tag = g_timeout_add (dbus_timeout_get_interval (timeout),
318                                timeout_handler, timeout);
319   
320   dbus_timeout_set_data (timeout, GUINT_TO_POINTER (timeout_tag), NULL);
321
322   return TRUE;
323 }
324
325 static void
326 remove_timeout (DBusTimeout *timeout,
327                 void        *data)
328 {
329   guint timeout_tag;
330   
331   timeout_tag = GPOINTER_TO_UINT (dbus_timeout_get_data (timeout));
332
333   if (timeout_tag != 0) /* if 0, probably timeout was disabled */
334     g_source_remove (timeout_tag);
335 }
336
337 static void
338 timeout_toggled (DBusTimeout *timeout,
339                  void        *data)
340 {
341   /* Because we just exit on OOM, enable/disable is
342    * no different from add/remove
343    */
344   if (dbus_timeout_get_enabled (timeout))
345     add_timeout (timeout, data);
346   else
347     remove_timeout (timeout, data);
348 }
349
350
351 static void
352 free_source (GSource *source)
353 {
354   g_source_destroy (source);
355 }
356
357 static void
358 wakeup_main (void *data)
359 {
360   g_main_context_wakeup (NULL);
361 }
362
363
364 /** @} */ /* End of GLib bindings internals */
365
366 /** @addtogroup DBusGLib
367  * @{
368  */
369
370 static GSource*
371 create_source (void         *connection_or_server,
372                GSourceFuncs *funcs)
373 {
374   GSource *source;
375   DBusGSource *dbus_source;
376
377   source = g_source_new (funcs, sizeof (DBusGSource));
378   
379   dbus_source = (DBusGSource *)source;  
380   dbus_source->watches = g_hash_table_new (NULL, NULL);
381   dbus_source->connection_or_server = connection_or_server;
382
383   return source;
384 }
385
386 /**
387  * Sets the watch and timeout functions of a #DBusConnection
388  * to integrate the connection with the GLib main loop.
389  *
390  * @param connection the connection
391  */
392 void
393 dbus_connection_setup_with_g_main (DBusConnection *connection)
394 {
395   GSource *source;
396
397   source = create_source (connection, &dbus_connection_funcs);
398
399   if (!dbus_connection_set_watch_functions (connection,
400                                             add_watch,
401                                             remove_watch,
402                                             watch_toggled,
403                                             source, NULL))
404     goto nomem;
405
406   if (!dbus_connection_set_timeout_functions (connection,
407                                               add_timeout,
408                                               remove_timeout,
409                                               timeout_toggled,
410                                               NULL, NULL))
411     goto nomem;
412     
413   dbus_connection_set_wakeup_main_function (connection,
414                                             wakeup_main,
415                                             NULL, NULL);
416       
417   g_source_attach (source, NULL);
418
419   g_static_mutex_lock (&connection_slot_lock);
420   if (connection_slot == -1 )
421     connection_slot = dbus_connection_allocate_data_slot ();
422   g_static_mutex_unlock (&connection_slot_lock);
423
424   if (connection_slot < 0)
425     goto nomem;
426
427   if (!dbus_connection_set_data (connection, connection_slot, source,
428                                  (DBusFreeFunction)free_source))
429     goto nomem;
430
431   return;
432
433  nomem:
434   g_error ("Not enough memory to set up DBusConnection for use with GLib");
435 }
436
437 /**
438  * Sets the watch and timeout functions of a #DBusServer
439  * to integrate the server with the GLib main loop.
440  *
441  * @param server the server
442  */
443 void
444 dbus_server_setup_with_g_main (DBusServer *server)
445 {
446   GSource *source;
447
448   source = create_source (server, &dbus_server_funcs);
449
450   dbus_server_set_watch_functions (server,
451                                    add_watch,
452                                    remove_watch,
453                                    watch_toggled,
454                                    source, NULL);
455
456   dbus_server_set_timeout_functions (server,
457                                      add_timeout,
458                                      remove_timeout,
459                                      timeout_toggled,
460                                      NULL, NULL);
461   
462   g_source_attach (source, NULL);
463
464   g_static_mutex_lock (&server_slot_lock);
465   if (server_slot == -1 )
466     server_slot = dbus_server_allocate_data_slot ();
467   g_static_mutex_unlock (&server_slot_lock);
468
469   if (server_slot < 0)
470     goto nomem;
471
472   if (!dbus_server_set_data (server, server_slot, source,
473                              (DBusFreeFunction)free_source))
474     goto nomem;
475
476   return;
477
478  nomem:
479   g_error ("Not enough memory to set up DBusServer for use with GLib");
480 }
481
482 /** @} */ /* end of public API */