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"
31 * @addtogroup DBusGLibInternals
35 static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
36 static GHashTable *info_hash = NULL;
39 wincaps_to_uscore (const char *caps)
44 str = g_string_new (NULL);
48 if (g_ascii_isupper (*p))
51 (str->len < 2 || str->str[str->len-2] != '_'))
52 g_string_append_c (str, '_');
53 g_string_append_c (str, g_ascii_tolower (*p));
57 g_string_append_c (str, *p);
62 return g_string_free (str, FALSE);
66 uscore_to_wincaps (const char *uscore)
70 gboolean last_was_uscore;
72 last_was_uscore = TRUE;
74 str = g_string_new (NULL);
78 if (*p == '-' || *p == '_')
80 last_was_uscore = TRUE;
86 g_string_append_c (str, g_ascii_toupper (*p));
87 last_was_uscore = FALSE;
90 g_string_append_c (str, *p);
95 return g_string_free (str, FALSE);
99 gobject_unregister_function (DBusConnection *connection,
104 object = G_OBJECT (user_data);
111 gtype_to_dbus_type (GType type)
117 return DBUS_TYPE_BYTE;
120 return DBUS_TYPE_BOOLEAN;
122 /* long gets cut to 32 bits so the remote API is consistent
123 * on all architectures
128 return DBUS_TYPE_INT32;
131 return DBUS_TYPE_UINT32;
134 return DBUS_TYPE_INT64;
137 return DBUS_TYPE_UINT64;
141 return DBUS_TYPE_DOUBLE;
144 return DBUS_TYPE_STRING;
147 return DBUS_TYPE_INVALID;
152 dbus_type_to_string (int type)
156 case DBUS_TYPE_INVALID:
160 case DBUS_TYPE_BOOLEAN:
162 case DBUS_TYPE_INT32:
164 case DBUS_TYPE_UINT32:
166 case DBUS_TYPE_DOUBLE:
168 case DBUS_TYPE_STRING:
170 case DBUS_TYPE_CUSTOM:
172 case DBUS_TYPE_ARRAY:
181 static DBusHandlerResult
182 handle_introspect (DBusConnection *connection,
183 DBusMessage *message,
188 unsigned int n_specs;
195 if (!dbus_message_get_path_decomposed (message, &path))
196 g_error ("Out of memory");
198 if (!dbus_connection_list_registered (connection, (const char**) path,
200 g_error ("Out of memory");
202 xml = g_string_new (NULL);
204 g_string_append (xml, "<node>\n");
206 last_type = G_TYPE_INVALID;
208 specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
214 GParamSpec *spec = specs[i];
220 dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
221 if (dbus_type == DBUS_TYPE_INVALID)
224 if (spec->owner_type != last_type)
226 if (last_type != G_TYPE_INVALID)
227 g_string_append (xml, " </interface>\n");
230 /* FIXME what should the namespace on the interface be in
231 * general? should people be able to set it for their
235 g_string_append (xml, " <interface name=\"org.gtk.objects.");
236 g_string_append (xml, g_type_name (spec->owner_type));
237 g_string_append (xml, "\">\n");
239 last_type = spec->owner_type;
242 can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
243 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
245 can_get = (spec->flags & G_PARAM_READABLE) != 0;
247 s = uscore_to_wincaps (spec->name);
251 g_string_append (xml, " <method name=\"set_");
252 g_string_append (xml, s);
253 g_string_append (xml, "\">\n");
255 g_string_append (xml, " <arg type=\"");
256 g_string_append (xml, dbus_type_to_string (dbus_type));
257 g_string_append (xml, "\"/>\n");
262 g_string_append (xml, " <method name=\"get_");
263 g_string_append (xml, s);
264 g_string_append (xml, "\">\n");
266 g_string_append (xml, " <arg type=\"");
267 g_string_append (xml, dbus_type_to_string (dbus_type));
268 g_string_append (xml, "\" direction=\"out\"/>\n");
277 if (last_type != G_TYPE_INVALID)
278 g_string_append (xml, " </interface>\n");
282 /* Append child nodes */
287 g_string_append_printf (xml, " <node name=\"%s\"/>\n",
292 /* Close the XML, and send it to the requesting app */
294 g_string_append (xml, "</node>\n");
296 ret = dbus_message_new_method_return (message);
298 g_error ("Out of memory");
300 dbus_message_append_args (message,
301 DBUS_TYPE_STRING, xml->str,
304 dbus_connection_send (connection, message, NULL);
305 dbus_message_unref (message);
307 g_string_free (xml, TRUE);
309 dbus_free_string_array (path);
310 dbus_free_string_array (children);
312 return DBUS_HANDLER_RESULT_HANDLED;
316 set_object_property (DBusConnection *connection,
317 DBusMessage *message,
322 DBusMessageIter iter;
327 dbus_message_iter_init (message, &iter);
328 type = dbus_message_get_type (message);
337 b = dbus_message_iter_get_byte (&iter);
339 g_value_init (&value, G_TYPE_UCHAR);
341 g_value_set_uchar (&value, b);
344 case DBUS_TYPE_BOOLEAN:
348 b = dbus_message_iter_get_boolean (&iter);
350 g_value_init (&value, G_TYPE_BOOLEAN);
352 g_value_set_boolean (&value, b);
355 case DBUS_TYPE_INT32:
359 i = dbus_message_iter_get_int32 (&iter);
361 g_value_init (&value, G_TYPE_INT);
363 g_value_set_int (&value, i);
366 case DBUS_TYPE_UINT32:
370 i = dbus_message_iter_get_uint32 (&iter);
372 g_value_init (&value, G_TYPE_UINT);
374 g_value_set_uint (&value, i);
377 case DBUS_TYPE_INT64:
381 i = dbus_message_iter_get_int64 (&iter);
383 g_value_init (&value, G_TYPE_INT64);
385 g_value_set_int64 (&value, i);
388 case DBUS_TYPE_UINT64:
392 i = dbus_message_iter_get_uint64 (&iter);
394 g_value_init (&value, G_TYPE_UINT64);
396 g_value_set_uint64 (&value, i);
399 case DBUS_TYPE_DOUBLE:
403 d = dbus_message_iter_get_double (&iter);
405 g_value_init (&value, G_TYPE_DOUBLE);
407 g_value_set_double (&value, d);
410 case DBUS_TYPE_STRING:
414 /* FIXME use a const string accessor */
416 s = dbus_message_iter_get_string (&iter);
418 g_value_init (&value, G_TYPE_STRING);
420 g_value_set_string (&value, s);
426 /* FIXME array and other types, especially byte array
427 * converted to G_TYPE_STRING
435 /* The g_object_set_property() will transform some types, e.g. it
436 * will let you use a uchar to set an int property etc. Note that
437 * any error in value range or value conversion will just
438 * g_warning(). These GObject skels are not for secure applications.
443 g_object_set_property (object,
447 ret = dbus_message_new_method_return (message);
449 g_error ("out of memory");
451 g_value_unset (&value);
455 ret = dbus_message_new_error (message,
456 DBUS_ERROR_INVALID_ARGS,
457 "Argument's D-BUS type can't be converted to a GType");
459 g_error ("out of memory");
466 get_object_property (DBusConnection *connection,
467 DBusMessage *message,
475 DBusMessageIter iter;
477 value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
479 ret = dbus_message_new_method_return (message);
481 g_error ("out of memory");
484 g_value_init (&value, value_type);
485 g_object_get_property (object, pspec->name, &value);
487 value_type = G_VALUE_TYPE (&value);
489 dbus_message_append_iter_init (message, &iter);
494 dbus_message_iter_append_byte (&iter,
495 g_value_get_char (&value));
498 dbus_message_iter_append_byte (&iter,
499 g_value_get_uchar (&value));
502 dbus_message_iter_append_boolean (&iter,
503 g_value_get_boolean (&value));
506 dbus_message_iter_append_int32 (&iter,
507 g_value_get_int (&value));
510 dbus_message_iter_append_uint32 (&iter,
511 g_value_get_uint (&value));
513 /* long gets cut to 32 bits so the remote API is consistent
514 * on all architectures
517 dbus_message_iter_append_int32 (&iter,
518 g_value_get_long (&value));
521 dbus_message_iter_append_uint32 (&iter,
522 g_value_get_ulong (&value));
525 dbus_message_iter_append_int64 (&iter,
526 g_value_get_int64 (&value));
529 dbus_message_iter_append_uint64 (&iter,
530 g_value_get_uint64 (&value));
533 dbus_message_iter_append_double (&iter,
534 g_value_get_float (&value));
537 dbus_message_iter_append_double (&iter,
538 g_value_get_double (&value));
541 /* FIXME, the GValue string may not be valid UTF-8 */
542 dbus_message_iter_append_string (&iter,
543 g_value_get_string (&value));
550 g_value_unset (&value);
554 dbus_message_unref (ret);
555 ret = dbus_message_new_error (message,
556 DBUS_ERROR_UNKNOWN_METHOD,
557 "Can't convert GType of object property to a D-BUS type");
563 static DBusHandlerResult
564 gobject_message_function (DBusConnection *connection,
565 DBusMessage *message,
568 const DBusGObjectInfo *info;
576 object = G_OBJECT (user_data);
578 if (dbus_message_is_method_call (message,
579 DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
581 return handle_introspect (connection, message, object);
583 member = dbus_message_get_member (message);
585 /* Try the metainfo, which lets us invoke methods */
587 g_static_mutex_lock (&info_hash_mutex);
588 /* FIXME this needs to walk up the inheritance tree, not
589 * just look at the most-derived class
591 info = g_hash_table_lookup (info_hash,
592 G_OBJECT_GET_CLASS (object));
593 g_static_mutex_unlock (&info_hash_mutex);
602 /* If no metainfo, we can still do properties and signals
603 * via standard GLib introspection
605 setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
606 getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
608 if (!(setter || getter))
609 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
611 s = wincaps_to_uscore (&member[4]);
613 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
624 ret = set_object_property (connection, message,
629 ret = get_object_property (connection, message,
634 g_assert_not_reached ();
638 g_assert (ret != NULL);
640 dbus_connection_send (connection, ret, NULL);
641 dbus_message_unref (ret);
642 return DBUS_HANDLER_RESULT_HANDLED;
645 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
648 static DBusObjectPathVTable gobject_dbus_vtable = {
649 gobject_unregister_function,
650 gobject_message_function,
654 /** @} */ /* end of internals */
657 * @addtogroup DBusGLib
662 * Install introspection information about the given object class
663 * sufficient to allow methods on the object to be invoked by name.
664 * The introspection information is normally generated by
665 * dbus-glib-tool, then this function is called in the
666 * class_init() for the object class.
668 * Once introspection information has been installed, instances of the
669 * object registered with dbus_connection_register_g_object() can have
670 * their methods invoked remotely.
672 * @param object_class class struct of the object
673 * @param info introspection data generated by dbus-glib-tool
676 dbus_g_object_class_install_info (GObjectClass *object_class,
677 const DBusGObjectInfo *info)
679 g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
681 g_static_mutex_lock (&info_hash_mutex);
683 if (info_hash == NULL)
685 info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
688 g_hash_table_replace (info_hash, object_class, (void*) info);
690 g_static_mutex_unlock (&info_hash_mutex);
694 * Registers a GObject at the given path. Properties, methods, and signals
695 * of the object can then be accessed remotely. Methods are only available
696 * if method introspection data has been added to the object's class
697 * with g_object_class_install_info().
699 * The registration will be cancelled if either the DBusConnection or
700 * the GObject gets finalized.
702 * @param connection the D-BUS connection
703 * @param at_path the path where the object will live (the object's name)
704 * @param object the object
707 dbus_connection_register_g_object (DBusConnection *connection,
713 g_return_if_fail (connection != NULL);
714 g_return_if_fail (at_path != NULL);
715 g_return_if_fail (G_IS_OBJECT (object));
717 split = _dbus_gutils_split_path (at_path);
719 if (!dbus_connection_register_object_path (connection,
720 (const char**) split,
721 &gobject_dbus_vtable,
723 g_error ("Failed to register GObject with DBusConnection");
727 /* FIXME set up memory management (so we break the
728 * registration if object or connection vanishes)
732 /** @} */ /* end of public API */
734 #ifdef DBUS_BUILD_TESTS
738 * @ingroup DBusGLibInternals
739 * Unit test for GLib GObject integration ("skeletons")
740 * @returns #TRUE on success.
743 _dbus_gobject_test (const char *test_data_dir)
746 static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
747 { "SetFoo", "set_foo" },
749 { "GetFooBar", "get_foo_bar" },
752 /* Impossible-to-handle cases */
753 /* { "FrobateUIHandler", "frobate_ui_handler" } */
757 while (i < (int) G_N_ELEMENTS (name_pairs))
762 uscore = wincaps_to_uscore (name_pairs[i].wincaps);
763 wincaps = uscore_to_wincaps (name_pairs[i].uscore);
765 if (strcmp (uscore, name_pairs[i].uscore) != 0)
767 g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
768 name_pairs[i].wincaps, name_pairs[i].uscore,
773 if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
775 g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
776 name_pairs[i].uscore, name_pairs[i].wincaps,
790 #endif /* DBUS_BUILD_TESTS */