Main Page   Modules   Data Structures   File List   Data Fields   Related Pages  

dbus-gobject.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-gobject.c Exporting a GObject remotely
00003  *
00004  * Copyright (C) 2003 Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.0
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-glib.h"
00026 #include "dbus-gtest.h"
00027 #include "dbus-gutils.h"
00028 #include "dbus-gvalue.h"
00029 #include <string.h>
00030 
00036 static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
00037 static GHashTable *info_hash = NULL;
00038 
00039 static char*
00040 wincaps_to_uscore (const char *caps)
00041 {
00042   const char *p;
00043   GString *str;
00044 
00045   str = g_string_new (NULL);
00046   p = caps;
00047   while (*p)
00048     {
00049       if (g_ascii_isupper (*p))
00050         {
00051           if (str->len > 0 &&
00052               (str->len < 2 || str->str[str->len-2] != '_'))
00053             g_string_append_c (str, '_');
00054           g_string_append_c (str, g_ascii_tolower (*p));
00055         }
00056       else
00057         {
00058           g_string_append_c (str, *p);
00059         }
00060       ++p;
00061     }
00062 
00063   return g_string_free (str, FALSE);
00064 }
00065 
00066 static char*
00067 uscore_to_wincaps (const char *uscore)
00068 {
00069   const char *p;
00070   GString *str;
00071   gboolean last_was_uscore;
00072 
00073   last_was_uscore = TRUE;
00074   
00075   str = g_string_new (NULL);
00076   p = uscore;
00077   while (*p)
00078     {
00079       if (*p == '-' || *p == '_')
00080         {
00081           last_was_uscore = TRUE;
00082         }
00083       else
00084         {
00085           if (last_was_uscore)
00086             {
00087               g_string_append_c (str, g_ascii_toupper (*p));
00088               last_was_uscore = FALSE;
00089             }
00090           else
00091             g_string_append_c (str, *p);
00092         }
00093       ++p;
00094     }
00095 
00096   return g_string_free (str, FALSE);
00097 }
00098 
00099 static void
00100 gobject_unregister_function (DBusConnection  *connection,
00101                              void            *user_data)
00102 {
00103   GObject *object;
00104 
00105   object = G_OBJECT (user_data);
00106 
00107   /* FIXME */
00108 
00109 }
00110 
00111 static int
00112 gtype_to_dbus_type (GType type)
00113 {
00114   switch (type)
00115     {
00116     case G_TYPE_CHAR:
00117     case G_TYPE_UCHAR:
00118       return DBUS_TYPE_BYTE;
00119       
00120     case G_TYPE_BOOLEAN:
00121       return DBUS_TYPE_BOOLEAN;
00122 
00123       /* long gets cut to 32 bits so the remote API is consistent
00124        * on all architectures
00125        */
00126       
00127     case G_TYPE_LONG:
00128     case G_TYPE_INT:
00129       return DBUS_TYPE_INT32;
00130     case G_TYPE_ULONG:
00131     case G_TYPE_UINT:
00132       return DBUS_TYPE_UINT32;
00133 
00134     case G_TYPE_INT64:
00135       return DBUS_TYPE_INT64;
00136 
00137     case G_TYPE_UINT64:
00138       return DBUS_TYPE_UINT64;
00139       
00140     case G_TYPE_FLOAT:
00141     case G_TYPE_DOUBLE:
00142       return DBUS_TYPE_DOUBLE;
00143 
00144     case G_TYPE_STRING:
00145       return DBUS_TYPE_STRING;
00146 
00147     default:
00148       return DBUS_TYPE_INVALID;
00149     }
00150 }
00151 
00152 static void
00153 introspect_properties (GObject *object, GString *xml)
00154 {
00155   unsigned int i;
00156   unsigned int n_specs;
00157   GType last_type;
00158   GParamSpec **specs;
00159 
00160   last_type = G_TYPE_INVALID;
00161   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
00162                                           &n_specs);
00163 
00164   for (i = 0; i < n_specs; i++ )
00165     {
00166       char *s;
00167       int dbus_type;
00168       gboolean can_set;
00169       gboolean can_get;
00170       GParamSpec *spec = specs[i];
00171       
00172       dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
00173       if (dbus_type == DBUS_TYPE_INVALID)
00174         continue;
00175       
00176       if (spec->owner_type != last_type)
00177         {
00178           if (last_type != G_TYPE_INVALID)
00179             g_string_append (xml, "  </interface>\n");
00180 
00181 
00182           /* FIXME what should the namespace on the interface be in
00183            * general?  should people be able to set it for their
00184            * objects?
00185            */
00186           g_string_append (xml, "  <interface name=\"org.gtk.objects.");
00187           g_string_append (xml, g_type_name (spec->owner_type));
00188           g_string_append (xml, "\">\n");
00189 
00190           last_type = spec->owner_type;
00191         }
00192 
00193       can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
00194                  (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
00195       
00196       can_get = (spec->flags & G_PARAM_READABLE) != 0;
00197 
00198       s = uscore_to_wincaps (spec->name);
00199       
00200       if (can_set)
00201         {
00202           g_string_append (xml, "    <method name=\"set_");
00203           g_string_append (xml, s);
00204           g_string_append (xml, "\">\n");
00205           
00206           g_string_append (xml, "      <arg type=\"");
00207           g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
00208           g_string_append (xml, "\"/>\n");
00209         }
00210 
00211       if (can_get)
00212         {
00213           g_string_append (xml, "    <method name=\"get_");
00214           g_string_append (xml, s);
00215           g_string_append (xml, "\">\n");
00216           
00217           g_string_append (xml, "      <arg type=\"");
00218           g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
00219           g_string_append (xml, "\" direction=\"out\"/>\n");
00220         }
00221 
00222       g_free (s);
00223     }
00224 
00225   if (last_type != G_TYPE_INVALID)
00226     g_string_append (xml, "  </interface>\n");
00227 
00228   g_free (specs);
00229 }
00230 
00231 static void
00232 introspect_signals (GType type, GString *xml)
00233 {
00234   guint i;
00235   guint *ids, n_ids;
00236 
00237   ids = g_signal_list_ids (type, &n_ids);
00238   if (!n_ids)
00239     return;
00240 
00241   g_string_append (xml, "  <interface name=\"org.gtk.objects.");
00242   g_string_append (xml, g_type_name (type));
00243   g_string_append (xml, "\">\n");
00244 
00245   /* FIXME: recurse to parent types ? */
00246   for (i = 0; i < n_ids; i++)
00247     {
00248       guint arg;
00249       GSignalQuery query;
00250       
00251       g_signal_query (ids[i], &query);
00252 
00253       if (query.return_type)
00254         continue; /* FIXME: these could be listed as methods ? */
00255 
00256       g_string_append (xml, "    <signal name=\"");
00257       g_string_append (xml, query.signal_name);
00258       g_string_append (xml, "\">\n");
00259 
00260       for (arg = 0; arg < query.n_params; arg++)
00261         {
00262           int dbus_type = gtype_to_dbus_type (query.param_types[arg]);
00263 
00264           g_string_append (xml, "      <arg type=\"");
00265           g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
00266           g_string_append (xml, "\"/>\n");
00267         }
00268 
00269       g_string_append (xml, "    </signal>\n");
00270     }
00271 
00272   g_string_append (xml, "  </interface>\n");
00273 }
00274 
00275 static DBusHandlerResult
00276 handle_introspect (DBusConnection *connection,
00277                    DBusMessage    *message,
00278                    GObject        *object)
00279 {
00280   GString *xml;
00281   unsigned int i;
00282   DBusMessage *ret;
00283   char **children;
00284   
00285   if (!dbus_connection_list_registered (connection, 
00286                                         dbus_message_get_path (message),
00287                                         &children))
00288     g_error ("Out of memory");
00289   
00290   xml = g_string_new (NULL);
00291 
00292   introspect_signals (G_OBJECT_TYPE (object), xml);
00293   introspect_properties (object, xml);
00294           
00295   g_string_append (xml, "<node>\n");
00296 
00297   /* Append child nodes */
00298   for (i = 0; children[i]; i++)
00299       g_string_append_printf (xml, "  <node name=\"%s\"/>\n",
00300                               children[i]);
00301   
00302   /* Close the XML, and send it to the requesting app */
00303   g_string_append (xml, "</node>\n");
00304 
00305   ret = dbus_message_new_method_return (message);
00306   if (ret == NULL)
00307     g_error ("Out of memory");
00308 
00309   dbus_message_append_args (message,
00310                             DBUS_TYPE_STRING, xml->str,
00311                             DBUS_TYPE_INVALID);
00312 
00313   dbus_connection_send (connection, message, NULL);
00314   dbus_message_unref (message);
00315 
00316   g_string_free (xml, TRUE);
00317 
00318   dbus_free_string_array (children);
00319   
00320   return DBUS_HANDLER_RESULT_HANDLED;
00321 }
00322 
00323 static DBusMessage*
00324 set_object_property (DBusConnection *connection,
00325                      DBusMessage    *message,
00326                      GObject        *object,
00327                      GParamSpec     *pspec)
00328 {
00329   GValue value = { 0, };
00330   DBusMessage *ret;
00331   DBusMessageIter iter;
00332 
00333   dbus_message_iter_init (message, &iter);
00334 
00335   /* The g_object_set_property() will transform some types, e.g. it
00336    * will let you use a uchar to set an int property etc. Note that
00337    * any error in value range or value conversion will just
00338    * g_warning(). These GObject skels are not for secure applications.
00339    */
00340   if (dbus_gvalue_demarshal (&iter, &value))
00341     {
00342       g_object_set_property (object,
00343                              pspec->name,
00344                              &value);
00345 
00346       g_value_unset (&value);
00347 
00348       ret = dbus_message_new_method_return (message);
00349       if (ret == NULL)
00350         g_error ("out of memory");
00351     }
00352   else
00353     {
00354       ret = dbus_message_new_error (message,
00355                                     DBUS_ERROR_INVALID_ARGS,
00356                                     "Argument's D-BUS type can't be converted to a GType");
00357       if (ret == NULL)
00358         g_error ("out of memory");
00359     }
00360 
00361   return ret;
00362 }
00363 
00364 static DBusMessage*
00365 get_object_property (DBusConnection *connection,
00366                      DBusMessage    *message,
00367                      GObject        *object,
00368                      GParamSpec     *pspec)
00369 {
00370   GType value_type;
00371   GValue value;
00372   DBusMessage *ret;
00373   DBusMessageIter iter;
00374 
00375   value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
00376 
00377   ret = dbus_message_new_method_return (message);
00378   if (ret == NULL)
00379     g_error ("out of memory");
00380 
00381   g_value_init (&value, value_type);
00382   g_object_get_property (object, pspec->name, &value);
00383 
00384   value_type = G_VALUE_TYPE (&value);
00385 
00386   dbus_message_append_iter_init (message, &iter);
00387 
00388   if (!dbus_gvalue_marshal (&iter, &value))
00389     {
00390       dbus_message_unref (ret);
00391       ret = dbus_message_new_error (message,
00392                                     DBUS_ERROR_UNKNOWN_METHOD,
00393                                     "Can't convert GType of object property to a D-BUS type");
00394     }
00395 
00396   return ret;
00397 }
00398 
00399 static DBusHandlerResult
00400 gobject_message_function (DBusConnection  *connection,
00401                           DBusMessage     *message,
00402                           void            *user_data)
00403 {
00404   const DBusGObjectInfo *info;
00405   GParamSpec *pspec;
00406   GObject *object;
00407   const char *member;
00408   gboolean setter;
00409   gboolean getter;
00410   char *s;
00411 
00412   object = G_OBJECT (user_data);
00413 
00414   if (dbus_message_is_method_call (message,
00415                                    DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
00416                                    "Introspect"))
00417     return handle_introspect (connection, message, object);
00418 
00419   member = dbus_message_get_member (message);
00420 
00421   /* Try the metainfo, which lets us invoke methods */
00422 
00423   g_static_mutex_lock (&info_hash_mutex);
00424   /* FIXME this needs to walk up the inheritance tree, not
00425    * just look at the most-derived class
00426    */
00427   info = g_hash_table_lookup (info_hash,
00428                               G_OBJECT_GET_CLASS (object));
00429   g_static_mutex_unlock (&info_hash_mutex);
00430 
00431   if (info != NULL)
00432     {
00433 
00434 
00435 
00436     }
00437 
00438   /* If no metainfo, we can still do properties and signals
00439    * via standard GLib introspection
00440    */
00441   setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
00442   getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
00443 
00444   if (!(setter || getter))
00445     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00446 
00447   s = wincaps_to_uscore (&member[4]);
00448 
00449   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
00450                                         s);
00451 
00452   g_free (s);
00453 
00454   if (pspec != NULL)
00455     {
00456       DBusMessage *ret;
00457 
00458       if (setter)
00459         {
00460           ret = set_object_property (connection, message,
00461                                      object, pspec);
00462         }
00463       else if (getter)
00464         {
00465           ret = get_object_property (connection, message,
00466                                      object, pspec);
00467         }
00468       else
00469         {
00470           g_assert_not_reached ();
00471           ret = NULL;
00472         }
00473 
00474       g_assert (ret != NULL);
00475 
00476       dbus_connection_send (connection, ret, NULL);
00477       dbus_message_unref (ret);
00478       return DBUS_HANDLER_RESULT_HANDLED;
00479     }
00480 
00481   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00482 }
00483 
00484 static DBusObjectPathVTable gobject_dbus_vtable = {
00485   gobject_unregister_function,
00486   gobject_message_function,
00487   NULL
00488 };
00489  /* end of internals */
00491 
00511 void
00512 dbus_g_object_class_install_info (GObjectClass          *object_class,
00513                                   const DBusGObjectInfo *info)
00514 {
00515   g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
00516 
00517   g_static_mutex_lock (&info_hash_mutex);
00518 
00519   if (info_hash == NULL)
00520     {
00521       info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
00522     }
00523 
00524   g_hash_table_replace (info_hash, object_class, (void*) info);
00525 
00526   g_static_mutex_unlock (&info_hash_mutex);
00527 }
00528 
00542 void
00543 dbus_connection_register_g_object (DBusConnection        *connection,
00544                                    const char            *at_path,
00545                                    GObject               *object)
00546 {
00547   g_return_if_fail (connection != NULL);
00548   g_return_if_fail (at_path != NULL);
00549   g_return_if_fail (G_IS_OBJECT (object));
00550 
00551   if (!dbus_connection_register_object_path (connection,
00552                                              at_path,
00553                                              &gobject_dbus_vtable,
00554                                              object))
00555     g_error ("Failed to register GObject with DBusConnection");
00556 
00557   /* FIXME set up memory management (so we break the
00558    * registration if object or connection vanishes)
00559    */
00560 }
00561  /* end of public API */
00563 
00564 #ifdef DBUS_BUILD_TESTS
00565 #include <stdlib.h>
00566 
00572 dbus_bool_t
00573 _dbus_gobject_test (const char *test_data_dir)
00574 {
00575   int i;
00576   static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
00577     { "SetFoo", "set_foo" },
00578     { "Foo", "foo" },
00579     { "GetFooBar", "get_foo_bar" },
00580     { "Hello", "hello" }
00581     
00582     /* Impossible-to-handle cases */
00583     /* { "FrobateUIHandler", "frobate_ui_handler" } */
00584   };
00585 
00586   i = 0;
00587   while (i < (int) G_N_ELEMENTS (name_pairs))
00588     {
00589       char *uscore;
00590       char *wincaps;
00591 
00592       uscore = wincaps_to_uscore (name_pairs[i].wincaps);
00593       wincaps = uscore_to_wincaps (name_pairs[i].uscore);
00594 
00595       if (strcmp (uscore, name_pairs[i].uscore) != 0)
00596         {
00597           g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
00598                       name_pairs[i].wincaps, name_pairs[i].uscore,
00599                       uscore);
00600           exit (1);
00601         }
00602       
00603       if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
00604         {
00605           g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
00606                       name_pairs[i].uscore, name_pairs[i].wincaps,
00607                       wincaps);
00608           exit (1);
00609         }
00610       
00611       g_free (uscore);
00612       g_free (wincaps);
00613 
00614       ++i;
00615     }
00616   
00617   return TRUE;
00618 }
00619 
00620 #endif /* DBUS_BUILD_TESTS */

Generated on Wed Jun 9 05:01:25 2004 for D-BUS by doxygen1.2.15