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

Generated on Wed Mar 30 21:15:20 2005 for D-BUS by  doxygen 1.4.1