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, 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 Tue Oct 12 02:07:52 2004 for D-BUS by doxygen 1.3.7