1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gobject.c Exporting a GObject remotely
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 2.0
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.
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.
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
25 #include "dbus-glib.h"
26 #include "dbus-gtest.h"
27 #include "dbus-gutils.h"
28 #include "dbus-gvalue.h"
32 * @addtogroup DBusGLibInternals
36 static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
37 static GHashTable *info_hash = NULL;
40 wincaps_to_uscore (const char *caps)
45 str = g_string_new (NULL);
49 if (g_ascii_isupper (*p))
52 (str->len < 2 || str->str[str->len-2] != '_'))
53 g_string_append_c (str, '_');
54 g_string_append_c (str, g_ascii_tolower (*p));
58 g_string_append_c (str, *p);
63 return g_string_free (str, FALSE);
67 uscore_to_wincaps (const char *uscore)
71 gboolean last_was_uscore;
73 last_was_uscore = TRUE;
75 str = g_string_new (NULL);
79 if (*p == '-' || *p == '_')
81 last_was_uscore = TRUE;
87 g_string_append_c (str, g_ascii_toupper (*p));
88 last_was_uscore = FALSE;
91 g_string_append_c (str, *p);
96 return g_string_free (str, FALSE);
100 gobject_unregister_function (DBusConnection *connection,
105 object = G_OBJECT (user_data);
112 gtype_to_dbus_type (GType type)
118 return DBUS_TYPE_BYTE;
121 return DBUS_TYPE_BOOLEAN;
123 /* long gets cut to 32 bits so the remote API is consistent
124 * on all architectures
129 return DBUS_TYPE_INT32;
132 return DBUS_TYPE_UINT32;
135 return DBUS_TYPE_INT64;
138 return DBUS_TYPE_UINT64;
142 return DBUS_TYPE_DOUBLE;
145 return DBUS_TYPE_STRING;
148 return DBUS_TYPE_INVALID;
153 introspect_properties (GObject *object, GString *xml)
156 unsigned int n_specs;
160 last_type = G_TYPE_INVALID;
161 specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
164 for (i = 0; i < n_specs; i++ )
170 GParamSpec *spec = specs[i];
172 dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
173 if (dbus_type == DBUS_TYPE_INVALID)
176 if (spec->owner_type != last_type)
178 if (last_type != G_TYPE_INVALID)
179 g_string_append (xml, " </interface>\n");
182 /* FIXME what should the namespace on the interface be in
183 * general? should people be able to set it for their
186 g_string_append (xml, " <interface name=\"org.gtk.objects.");
187 g_string_append (xml, g_type_name (spec->owner_type));
188 g_string_append (xml, "\">\n");
190 last_type = spec->owner_type;
193 can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
194 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
196 can_get = (spec->flags & G_PARAM_READABLE) != 0;
198 s = uscore_to_wincaps (spec->name);
202 g_string_append (xml, " <method name=\"set_");
203 g_string_append (xml, s);
204 g_string_append (xml, "\">\n");
206 g_string_append (xml, " <arg type=\"");
207 g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
208 g_string_append (xml, "\"/>\n");
213 g_string_append (xml, " <method name=\"get_");
214 g_string_append (xml, s);
215 g_string_append (xml, "\">\n");
217 g_string_append (xml, " <arg type=\"");
218 g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
219 g_string_append (xml, "\" direction=\"out\"/>\n");
225 if (last_type != G_TYPE_INVALID)
226 g_string_append (xml, " </interface>\n");
232 introspect_signals (GType type, GString *xml)
237 ids = g_signal_list_ids (type, &n_ids);
241 g_string_append (xml, " <interface name=\"org.gtk.objects.");
242 g_string_append (xml, g_type_name (type));
243 g_string_append (xml, "\">\n");
245 /* FIXME: recurse to parent types ? */
246 for (i = 0; i < n_ids; i++)
251 g_signal_query (ids[i], &query);
253 if (query.return_type)
254 continue; /* FIXME: these could be listed as methods ? */
256 g_string_append (xml, " <signal name=\"");
257 g_string_append (xml, query.signal_name);
258 g_string_append (xml, "\">\n");
260 for (arg = 0; arg < query.n_params; arg++)
262 int dbus_type = gtype_to_dbus_type (query.param_types[arg]);
264 g_string_append (xml, " <arg type=\"");
265 g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
266 g_string_append (xml, "\"/>\n");
269 g_string_append (xml, " </signal>\n");
272 g_string_append (xml, " </interface>\n");
275 static DBusHandlerResult
276 handle_introspect (DBusConnection *connection,
277 DBusMessage *message,
286 if (!dbus_message_get_path_decomposed (message, &path))
287 g_error ("Out of memory");
289 if (!dbus_connection_list_registered (connection, (const char**) path,
291 g_error ("Out of memory");
293 xml = g_string_new (NULL);
295 introspect_signals (G_OBJECT_TYPE (object), xml);
296 introspect_properties (object, xml);
298 g_string_append (xml, "<node>\n");
300 /* Append child nodes */
301 for (i = 0; children[i]; i++)
302 g_string_append_printf (xml, " <node name=\"%s\"/>\n",
305 /* Close the XML, and send it to the requesting app */
306 g_string_append (xml, "</node>\n");
308 ret = dbus_message_new_method_return (message);
310 g_error ("Out of memory");
312 dbus_message_append_args (message,
313 DBUS_TYPE_STRING, xml->str,
316 dbus_connection_send (connection, message, NULL);
317 dbus_message_unref (message);
319 g_string_free (xml, TRUE);
321 dbus_free_string_array (path);
322 dbus_free_string_array (children);
324 return DBUS_HANDLER_RESULT_HANDLED;
328 set_object_property (DBusConnection *connection,
329 DBusMessage *message,
333 GValue value = { 0, };
335 DBusMessageIter iter;
337 dbus_message_iter_init (message, &iter);
339 /* The g_object_set_property() will transform some types, e.g. it
340 * will let you use a uchar to set an int property etc. Note that
341 * any error in value range or value conversion will just
342 * g_warning(). These GObject skels are not for secure applications.
344 if (dbus_gvalue_demarshal (&iter, &value))
346 g_object_set_property (object,
350 g_value_unset (&value);
352 ret = dbus_message_new_method_return (message);
354 g_error ("out of memory");
358 ret = dbus_message_new_error (message,
359 DBUS_ERROR_INVALID_ARGS,
360 "Argument's D-BUS type can't be converted to a GType");
362 g_error ("out of memory");
369 get_object_property (DBusConnection *connection,
370 DBusMessage *message,
377 DBusMessageIter iter;
379 value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
381 ret = dbus_message_new_method_return (message);
383 g_error ("out of memory");
385 g_value_init (&value, value_type);
386 g_object_get_property (object, pspec->name, &value);
388 value_type = G_VALUE_TYPE (&value);
390 dbus_message_append_iter_init (message, &iter);
392 if (!dbus_gvalue_marshal (&iter, &value))
394 dbus_message_unref (ret);
395 ret = dbus_message_new_error (message,
396 DBUS_ERROR_UNKNOWN_METHOD,
397 "Can't convert GType of object property to a D-BUS type");
403 static DBusHandlerResult
404 gobject_message_function (DBusConnection *connection,
405 DBusMessage *message,
408 const DBusGObjectInfo *info;
416 object = G_OBJECT (user_data);
418 if (dbus_message_is_method_call (message,
419 DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
421 return handle_introspect (connection, message, object);
423 member = dbus_message_get_member (message);
425 /* Try the metainfo, which lets us invoke methods */
427 g_static_mutex_lock (&info_hash_mutex);
428 /* FIXME this needs to walk up the inheritance tree, not
429 * just look at the most-derived class
431 info = g_hash_table_lookup (info_hash,
432 G_OBJECT_GET_CLASS (object));
433 g_static_mutex_unlock (&info_hash_mutex);
442 /* If no metainfo, we can still do properties and signals
443 * via standard GLib introspection
445 setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
446 getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
448 if (!(setter || getter))
449 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
451 s = wincaps_to_uscore (&member[4]);
453 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
464 ret = set_object_property (connection, message,
469 ret = get_object_property (connection, message,
474 g_assert_not_reached ();
478 g_assert (ret != NULL);
480 dbus_connection_send (connection, ret, NULL);
481 dbus_message_unref (ret);
482 return DBUS_HANDLER_RESULT_HANDLED;
485 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
488 static DBusObjectPathVTable gobject_dbus_vtable = {
489 gobject_unregister_function,
490 gobject_message_function,
494 /** @} */ /* end of internals */
497 * @addtogroup DBusGLib
502 * Install introspection information about the given object class
503 * sufficient to allow methods on the object to be invoked by name.
504 * The introspection information is normally generated by
505 * dbus-glib-tool, then this function is called in the
506 * class_init() for the object class.
508 * Once introspection information has been installed, instances of the
509 * object registered with dbus_connection_register_g_object() can have
510 * their methods invoked remotely.
512 * @param object_class class struct of the object
513 * @param info introspection data generated by dbus-glib-tool
516 dbus_g_object_class_install_info (GObjectClass *object_class,
517 const DBusGObjectInfo *info)
519 g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
521 g_static_mutex_lock (&info_hash_mutex);
523 if (info_hash == NULL)
525 info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
528 g_hash_table_replace (info_hash, object_class, (void*) info);
530 g_static_mutex_unlock (&info_hash_mutex);
534 * Registers a GObject at the given path. Properties, methods, and signals
535 * of the object can then be accessed remotely. Methods are only available
536 * if method introspection data has been added to the object's class
537 * with g_object_class_install_info().
539 * The registration will be cancelled if either the DBusConnection or
540 * the GObject gets finalized.
542 * @param connection the D-BUS connection
543 * @param at_path the path where the object will live (the object's name)
544 * @param object the object
547 dbus_connection_register_g_object (DBusConnection *connection,
553 g_return_if_fail (connection != NULL);
554 g_return_if_fail (at_path != NULL);
555 g_return_if_fail (G_IS_OBJECT (object));
557 split = _dbus_gutils_split_path (at_path);
559 if (!dbus_connection_register_object_path (connection,
560 (const char**) split,
561 &gobject_dbus_vtable,
563 g_error ("Failed to register GObject with DBusConnection");
567 /* FIXME set up memory management (so we break the
568 * registration if object or connection vanishes)
572 /** @} */ /* end of public API */
574 #ifdef DBUS_BUILD_TESTS
578 * @ingroup DBusGLibInternals
579 * Unit test for GLib GObject integration ("skeletons")
580 * @returns #TRUE on success.
583 _dbus_gobject_test (const char *test_data_dir)
586 static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
587 { "SetFoo", "set_foo" },
589 { "GetFooBar", "get_foo_bar" },
592 /* Impossible-to-handle cases */
593 /* { "FrobateUIHandler", "frobate_ui_handler" } */
597 while (i < (int) G_N_ELEMENTS (name_pairs))
602 uscore = wincaps_to_uscore (name_pairs[i].wincaps);
603 wincaps = uscore_to_wincaps (name_pairs[i].uscore);
605 if (strcmp (uscore, name_pairs[i].uscore) != 0)
607 g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
608 name_pairs[i].wincaps, name_pairs[i].uscore,
613 if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
615 g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
616 name_pairs[i].uscore, name_pairs[i].wincaps,
630 #endif /* DBUS_BUILD_TESTS */