diff options
author | Colin Walters <walters@verbum.org> | 2005-02-17 17:41:30 +0000 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2005-02-17 17:41:30 +0000 |
commit | 03f6615eaccc2c1f84d4252e5a627a2ac86d41d9 (patch) | |
tree | 6b7867f43ae7858f2aae4a208e01d10dd3d752a3 /glib | |
parent | 9e4450872a6861bd93a01dabe14d2d16f6c84d3f (diff) |
2005-02-17 Colin Walters <walters@verbum.org>
This patch is based on initial work from
Paul Kuliniewicz <kuliniew@purdue.edu>.
* glib/dbus-gvalue.c (dbus_gvalue_init): New function; move
initialization of GValue from dbus type to here.
(dbus_gvalue_genmarshal_name_from_type): New function; generates a string
for the "glib-genmarshal" program from a DBus type.
(dbus_gvalue_binding_type_from_type): New function; turns a DBus type
into the C name for it we use in the glib bindings.
(dbus_gvalue_ctype_from_type): New function; maps a DBus type into a
glib C type (not GValue).
(dbus_gvalue_demarshal): invoke dbus_gvalue_init.
* glib/dbus-gutils.c (_dbus_gutils_wincaps_to_uscore): Moved here
from dbus-gobject.c.
* glib/dbus-gutils.h: Prototype it.
* glib/dbus-gproxy.c: Include new dbus-gobject.h.
(marshal_dbus_message_to_g_marshaller): Use new shared function
dbus_glib_marshal_dbus_message_to_gvalue_array.
* glib/dbus-gparser.c (parse_interface, parse_method): Handle c_name attribute.
Will be changed once we have annotations.
* glib/dbus-gobject.c: Change info_hash_mutex from GStaticMutex to
GStaticRWLock. Callers updated.
(wincaps_to_uscore): Move to dbus-gutils.c. Callers updated.
(string_table_next): New function for iterating over zero-terminated
string value array.
(string_table_lookup): New function; retrieves specific entry in
array.
(get_method_data): New function; look up method data in object data chunk.
(object_error_domain_prefix_from_object_info)
(object_error_code_from_object_info): New functions, but not implemented yet.
(method_interface_from_object_info): New function; retrieve interface name.
(method_name_from_object_info): New function; retrieve method name.
(method_arg_info_from_object_info): New function; retrieve argument data.
(arg_iterate): New function; iterates over serialized argument data.
(method_dir_signature_from_object_info): New function; returns a
GString holding type signature for arguments for just one
direction (input or output).
(method_input_signature_from_object_info)
(method_output_signature_from_object_info): New functions.
(dbus_glib_marshal_dbus_message_to_gvalue_array): New shared function;
converts dbus message arguments into a GValue array. Used for both
signal handling and method invocation.
(struct DBusGlibWriteIterfaceData): New utility structure.
(write_interface): New function; generate introspection XML for
an interface.
(introspect_interfaces): New function; gathers all interface->methods,
generates introspection XML for them.
(handle_introspect): Invoke introspect_interfaces.
(get_object_property): Be sure to zero-initalize stack-allocated GValue.
(lookup_object_and_method): New function; examines an incoming message
and attempts to match it up (via interface, method name, and argument
signature) with a known object and method.
(gerror_domaincode_to_dbus_error_name): New function; converts a
GError domain and code into a DBus error name. Needs GError data
added to object introspection to work well.
(gerror_to_dbus_error_message): Creates a DBusMessage error return from
GError.
(invoke_object_method): New function to invoke an object method
looked up via lookup_object_and_method. Parses the incoming
message, turns it into a GValue array, then invokes the marshaller
specified in the DBusGMethodInfo. Creates a new message with
either return values or error message as appropriate.
(gobject_message_function): Invoke lookup_object_and_method and
invoke_object_method.
* glib/dbus-glib-tool.c: Include dbus-binding-tool-glib.h.
(enum DBusBindingOutputMode): New enum for binding output modes.
(pretty_print): Print binding names.
(dbus_binding_tool_error_quark): GError bits.
(version): Fix typo.
(main): Create GIOChannel for output. Parse new --mode argument,
possible values are "pretty-print", "glib-server", "glib-client".
Use mode to invoke appropriate function.
* glib/dbus-gobject.h: Prototype dbus_glib_marshal_dbus_message_to_gvalue_array.
* glib/dbus-glib-tool.h: New header, just includes GError bits
for now.
* glib/dbus-gidl.c (struct InterfaceInfo): Add bindings hashtable;
maps binding style to name.
(struct MethodInfo): Ditto.
(get_hash_keys, get_hash_key): Utility function, returns keys for
a GHashTable.
(interface_info_new, method_info_new): Initialize bindings.
(interface_info_unref, method_info_unref): Destroy bindings.
(method_info_get_binding_names, method_info_get_binding_name)
(interface_info_get_binding_names, interface_info_get_binding_name):
Functions for retrieving binding names.
(method_info_set_binding_name, interface_info_set_binding_name):
Functions for setting binding names.
* glib/dbus-binding-tool-glib.h: New file, has prototypes
for glib binding generation.
* glib/dbus-binding-tool-glib.c: New file, implements server-side
and client-side glib glue generation.
* glib/Makefile.am (dbus_binding_tool_SOURCES): Add
dbus-binding-tool-glib.c, dbus-binding-tool-glib.h,
dbus-glib-tool.h.
* dbus/dbus-glib.h (struct DBusGMethodMarshaller): Remove in favor
of using GClosureMarshal directly.
(struct DBusGObjectInfo): Add n_infos member.
* test/glib/test-service-glib.xml: New file; contains introspection data
for MyTestObject used in test-service-glib.c.
* test/glib/test-service-glib.c (enum MyObjectError): New GError enum.
(my_object_do_nothing, my_object_increment, my_object_throw_error)
(my_object_uppercase, my_object_many_args): New test methods.
(main): Use dbus_g_object_class_install_info to include generated object
info.
* test/glib/Makefile.am: Generate server-side glue for test-service-glib.c,
as well as client-side bindings.
* test/glib/test-dbus-glib.c: Include test-service-glib-bindings.h.
(main): Activate TestSuiteGLibService; test invoke a bunch of its methods
using both the dbus_gproxy stuff directly as well as the generated bindings.
Diffstat (limited to 'glib')
-rw-r--r-- | glib/Makefile.am | 3 | ||||
-rw-r--r-- | glib/dbus-binding-tool-glib.c | 812 | ||||
-rw-r--r-- | glib/dbus-binding-tool-glib.h | 33 | ||||
-rw-r--r-- | glib/dbus-gidl.c | 73 | ||||
-rw-r--r-- | glib/dbus-gidl.h | 14 | ||||
-rw-r--r-- | glib/dbus-glib-tool.c | 120 | ||||
-rw-r--r-- | glib/dbus-glib-tool.h | 37 | ||||
-rw-r--r-- | glib/dbus-gobject.c | 596 | ||||
-rw-r--r-- | glib/dbus-gobject.h | 35 | ||||
-rw-r--r-- | glib/dbus-gparser.c | 8 | ||||
-rw-r--r-- | glib/dbus-gproxy.c | 52 | ||||
-rw-r--r-- | glib/dbus-gutils.c | 28 | ||||
-rw-r--r-- | glib/dbus-gutils.h | 1 | ||||
-rw-r--r-- | glib/dbus-gvalue.c | 171 | ||||
-rw-r--r-- | glib/dbus-gvalue.h | 28 |
15 files changed, 1889 insertions, 122 deletions
diff --git a/glib/Makefile.am b/glib/Makefile.am index 6a42e6f9..9afe4a72 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -39,6 +39,9 @@ libdbus_gtool_la_LIBADD = libdbus-glib-1.la bin_PROGRAMS=dbus-binding-tool dbus_binding_tool_SOURCES = \ + dbus-binding-tool-glib.h \ + dbus-binding-tool-glib.c \ + dbus-glib-tool.h \ dbus-glib-tool.c dbus_binding_tool_LDADD= -lexpat libdbus-gtool.la diff --git a/glib/dbus-binding-tool-glib.c b/glib/dbus-binding-tool-glib.c new file mode 100644 index 00000000..98c13341 --- /dev/null +++ b/glib/dbus-binding-tool-glib.c @@ -0,0 +1,812 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-binding-tool-glib.c: Output C glue + * + * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> +#include "dbus-gidl.h" +#include "dbus-gparser.h" +#include "dbus-gutils.h" +#include "dbus-gvalue.h" +#include "dbus-glib-tool.h" +#include "dbus-binding-tool-glib.h" +#include <glib/gi18n.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define MARSHAL_PREFIX "dbus_glib_marshal" + +typedef struct +{ + GIOChannel *channel; + + GError **error; + + GHashTable *generated; +} DBusBindingToolCData; + +static gboolean gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error); +static gboolean generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error); +static gboolean generate_client_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error); + +static char * +compute_marshaller (MethodInfo *method, GError **error) +{ + GSList *elt; + GString *ret; + gboolean first; + + /* All methods required to return boolean for now; + * will be conditional on method info later */ + ret = g_string_new ("BOOLEAN:"); + + first = TRUE; + /* Append input arguments */ + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + + if (arg_info_get_direction (arg) == ARG_IN) + { + const char *marshal_name = dbus_gvalue_genmarshal_name_from_type (arg_info_get_type (arg)); + if (!marshal_name) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type %d to glib-genmarshal type"), + arg_info_get_type (arg)); + g_string_free (ret, TRUE); + return NULL; + } + if (!first) + g_string_append (ret, ","); + else + first = FALSE; + g_string_append (ret, marshal_name); + } + } + + /* Append pointer for each out arg storage */ + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + + if (arg_info_get_direction (arg) == ARG_OUT) + { + if (!first) + g_string_append (ret, ","); + else + first = FALSE; + g_string_append (ret, "POINTER"); + } + } + + /* Final GError parameter */ + if (!first) + g_string_append (ret, ","); + g_string_append (ret, "POINTER"); + + return g_string_free (ret, FALSE); + +} + +static char * +compute_marshaller_name (MethodInfo *method, GError **error) +{ + GSList *elt; + GString *ret; + + /* All methods required to return boolean for now; + * will be conditional on method info later */ + ret = g_string_new (MARSHAL_PREFIX "_BOOLEAN_"); + + /* Append input arguments */ + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + + if (arg_info_get_direction (arg) == ARG_IN) + { + const char *marshal_name; + int type; + + type = arg_info_get_type (arg); + marshal_name = dbus_gvalue_genmarshal_name_from_type (type); + if (!marshal_name) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type %d to glib-genmarshal type"), + type); + g_string_free (ret, TRUE); + return NULL; + } + + g_string_append (ret, "_"); + g_string_append (ret, dbus_gvalue_genmarshal_name_from_type (arg_info_get_type (arg))); + } + } + + /* Append pointer for each out arg storage */ + for (elt = method_info_get_args (method); elt; elt = elt->next) + { + ArgInfo *arg = elt->data; + + if (arg_info_get_direction (arg) == ARG_OUT) + { + g_string_append (ret, "_POINTER"); + } + } + + /* Final GError parameter */ + g_string_append (ret, "_POINTER"); + + return g_string_free (ret, FALSE); +} + +static gboolean +gather_marshallers_list (GSList *list, DBusBindingToolCData *data, GError **error) +{ + GSList *tmp; + + tmp = list; + while (tmp != NULL) + { + if (!gather_marshallers (tmp->data, data, error)) + return FALSE; + tmp = tmp->next; + } + return TRUE; +} + +static gboolean +gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error) +{ + if (base_info_get_type (base) == INFO_TYPE_NODE) + { + if (!gather_marshallers_list (node_info_get_nodes ((NodeInfo *) base), + data, error)) + return FALSE; + if (!gather_marshallers_list (node_info_get_interfaces ((NodeInfo *) base), + data, error)) + return FALSE; + } + else + { + InterfaceInfo *interface; + GSList *methods; + GSList *tmp; + const char *interface_c_name; + + interface = (InterfaceInfo *) base; + interface_c_name = interface_info_get_binding_name (interface, "C"); + if (interface_c_name == NULL) + { + return TRUE; + } + + methods = interface_info_get_methods (interface); + + /* Generate the necessary marshallers for the methods. */ + + for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp)) + { + MethodInfo *method; + char *marshaller_name; + + method = (MethodInfo *) tmp->data; + if (method_info_get_binding_name (method, "C") == NULL) + { + continue; + } + + marshaller_name = compute_marshaller (method, error); + if (!marshaller_name) + return FALSE; + + if (g_hash_table_lookup (data->generated, marshaller_name)) + { + g_free (marshaller_name); + continue; + } + + g_hash_table_insert (data->generated, marshaller_name, NULL); + } + + } + return TRUE; +} + +static gboolean +generate_glue_list (GSList *list, DBusBindingToolCData *data, GError **error) +{ + GSList *tmp; + + tmp = list; + while (tmp != NULL) + { + if (!generate_glue (tmp->data, data, error)) + return FALSE; + tmp = tmp->next; + } + return TRUE; +} + +#define WRITE_OR_LOSE(x) do { gsize bytes_written; if (!g_io_channel_write_chars (channel, x, -1, &bytes_written, error)) goto io_lose; } while (0) + +static gboolean +write_printf_to_iochannel (const char *fmt, GIOChannel *channel, GError **error, ...) +{ + char *str; + va_list args; + GIOStatus status; + gsize written; + gboolean ret; + + va_start (args, error); + + str = g_strdup_vprintf (fmt, args); + if ((status = g_io_channel_write_chars (channel, str, -1, &written, error)) == G_IO_STATUS_NORMAL) + ret = TRUE; + else + ret = FALSE; + + g_free (str); + + va_end (args); + + return ret; +} + +static gboolean +generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error) +{ + if (base_info_get_type (base) == INFO_TYPE_NODE) + { + if (!generate_glue_list (node_info_get_nodes ((NodeInfo *) base), + data, error)) + return FALSE; + if (!generate_glue_list (node_info_get_interfaces ((NodeInfo *) base), + data, error)) + return FALSE; + } + else + { + GIOChannel *channel; + InterfaceInfo *interface; + GSList *methods; + GSList *tmp; + gsize i; + int count; + const char *interface_c_name; + GString *object_introspection_data_blob; + + channel = data->channel; + + interface = (InterfaceInfo *) base; + interface_c_name = interface_info_get_binding_name (interface, "C"); + if (interface_c_name == NULL) + { + return TRUE; + } + + object_introspection_data_blob = g_string_new_len ("", 0); + + methods = interface_info_get_methods (interface); + count = 0; + + /* Table of marshalled methods. */ + + if (!write_printf_to_iochannel ("static const DBusGMethodInfo dbus_glib_%s_methods[] = {\n", channel, error, interface_info_get_binding_name (interface, "C"))) + goto io_lose; + for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp)) + { + MethodInfo *method; + char *marshaller_name; + const char *method_c_name; + GSList *args; + + method = (MethodInfo *) tmp->data; + method_c_name = method_info_get_binding_name (method, "C"); + if (method_c_name == NULL) + { + continue; + } + + if (!write_printf_to_iochannel (" { (GCallback) %s, ", channel, error, + method_c_name)) + goto io_lose; + + marshaller_name = compute_marshaller_name (method, error); + if (!marshaller_name) + goto io_lose; + + if (!write_printf_to_iochannel ("%s, %d },\n", channel, error, + marshaller_name, + object_introspection_data_blob->len)) + { + g_free (marshaller_name); + goto io_lose; + } + + /* Object method data blob format: + * <iface>\0<name>\0(<argname>\0<argdirection>\0<argtype>\0)*\0 + */ + + g_string_append (object_introspection_data_blob, interface_info_get_name (interface)); + g_string_append_c (object_introspection_data_blob, '\0'); + + g_string_append (object_introspection_data_blob, method_info_get_name (method)); + g_string_append_c (object_introspection_data_blob, '\0'); + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + char direction; + + arg = args->data; + + g_string_append (object_introspection_data_blob, arg_info_get_name (arg)); + g_string_append_c (object_introspection_data_blob, '\0'); + + switch (arg_info_get_direction (arg)) + { + case ARG_IN: + direction = 'I'; + break; + case ARG_OUT: + direction = 'O'; + break; + case ARG_INVALID: + break; + } + g_string_append_c (object_introspection_data_blob, direction); + g_string_append_c (object_introspection_data_blob, '\0'); + + g_string_append_c (object_introspection_data_blob, arg_info_get_type (arg)); + g_string_append_c (object_introspection_data_blob, '\0'); + } + + g_string_append_c (object_introspection_data_blob, '\0'); + + count++; + } + WRITE_OR_LOSE ("};\n\n"); + + /* Information about the object. */ + + if (!write_printf_to_iochannel ("const DBusGObjectInfo dbus_glib_%s_object_info = {\n", + channel, error, interface_c_name)) + goto io_lose; + WRITE_OR_LOSE (" 0,\n"); + if (!write_printf_to_iochannel (" dbus_glib_%s_methods,\n", channel, error, interface_c_name)) + goto io_lose; + if (!write_printf_to_iochannel (" %d,\n", channel, error, count)) + goto io_lose; + WRITE_OR_LOSE(" \""); + for (i = 0; i < object_introspection_data_blob->len; i++) + { + if (object_introspection_data_blob->str[i] != '\0') + { + if (!g_io_channel_write_chars (channel, object_introspection_data_blob->str + i, 1, NULL, error)) + return FALSE; + } + else + { + if (!g_io_channel_write_chars (channel, "\\0", -1, NULL, error)) + return FALSE; + } + } + WRITE_OR_LOSE ("\"\n};\n\n"); + + g_string_free (object_introspection_data_blob, TRUE); + } + return TRUE; + io_lose: + return FALSE; +} + +static void +write_marshaller (gpointer key, gpointer value, gpointer user_data) +{ + DBusBindingToolCData *data; + const char *marshaller; + gsize bytes_written; + + data = user_data; + marshaller = key; + + if (data->error && *data->error) + return; + + if (g_io_channel_write_chars (data->channel, marshaller, -1, &bytes_written, data->error) == G_IO_STATUS_NORMAL) + g_io_channel_write_chars (data->channel, "\n", -1, &bytes_written, data->error); +} + +gboolean +dbus_binding_tool_output_glib_server (BaseInfo *info, GIOChannel *channel, GError **error) +{ + gboolean ret; + GPtrArray *argv; + gint child_stdout; + GIOChannel *genmarshal_stdout; + GPid child_pid; + DBusBindingToolCData data; + char *tempfile_name; + gint tempfile_fd; + GIOStatus iostatus; + char buf[4096]; + gsize bytes_read, bytes_written; + + memset (&data, 0, sizeof (data)); + + data.generated = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); + data.error = error; + genmarshal_stdout = NULL; + tempfile_name = NULL; + + if (!gather_marshallers (info, &data, error)) + goto io_lose; + + tempfile_fd = g_file_open_tmp ("dbus-binding-tool-c-marshallers.XXXXXX", + &tempfile_name, error); + if (tempfile_fd < 0) + goto io_lose; + + data.channel = g_io_channel_unix_new (tempfile_fd); + if (!g_io_channel_set_encoding (data.channel, NULL, error)) + goto io_lose; + g_hash_table_foreach (data.generated, write_marshaller, &data); + if (error && *error != NULL) + { + ret = FALSE; + g_io_channel_close (data.channel); + g_io_channel_unref (data.channel); + goto io_lose; + } + + g_io_channel_close (data.channel); + g_io_channel_unref (data.channel); + + /* Now spawn glib-genmarshal to insert all our required marshallers */ + argv = g_ptr_array_new (); + g_ptr_array_add (argv, "glib-genmarshal"); + g_ptr_array_add (argv, "--header"); + g_ptr_array_add (argv, "--body"); + g_ptr_array_add (argv, "--prefix=" MARSHAL_PREFIX); + g_ptr_array_add (argv, tempfile_name); + g_ptr_array_add (argv, NULL); + if (!g_spawn_async_with_pipes (NULL, (char**)argv->pdata, NULL, + G_SPAWN_SEARCH_PATH, + NULL, NULL, + &child_pid, + NULL, + &child_stdout, NULL, error)) + { + g_ptr_array_free (argv, TRUE); + goto io_lose; + } + g_ptr_array_free (argv, TRUE); + + genmarshal_stdout = g_io_channel_unix_new (child_stdout); + if (!g_io_channel_set_encoding (genmarshal_stdout, NULL, error)) + goto io_lose; + + WRITE_OR_LOSE ("/* Generated by dbus-binding-tool; do not edit! */\n\n"); + + while ((iostatus = g_io_channel_read_chars (genmarshal_stdout, buf, sizeof (buf), + &bytes_read, error)) == G_IO_STATUS_NORMAL) + if (g_io_channel_write_chars (channel, buf, bytes_read, &bytes_written, error) != G_IO_STATUS_NORMAL) + goto io_lose; + if (iostatus != G_IO_STATUS_EOF) + goto io_lose; + + g_io_channel_close (genmarshal_stdout); + + WRITE_OR_LOSE ("#include <dbus/dbus-glib.h>\n"); + + g_io_channel_ref (data.channel); + data.channel = channel; + if (!generate_glue (info, &data, error)) + goto io_lose; + + ret = TRUE; + cleanup: + if (tempfile_name) + unlink (tempfile_name); + g_free (tempfile_name); + if (genmarshal_stdout) + g_io_channel_unref (genmarshal_stdout); + if (data.channel) + g_io_channel_unref (data.channel); + g_hash_table_destroy (data.generated); + + return ret; + io_lose: + ret = FALSE; + goto cleanup; +} + +static char * +iface_to_c_prefix (const char *iface) +{ + char **components; + char **component; + GString *ret; + gboolean first; + + components = g_strsplit (iface, ".", 0); + + first = TRUE; + ret = g_string_new (""); + for (component = components; *component; component++) + { + if (!first) + g_string_append_c (ret, '_'); + else + first = FALSE; + g_string_append (ret, *component); + } + g_strfreev (components); + return g_string_free (ret, FALSE); +} + +static char * +compute_client_method_name (InterfaceInfo *iface, MethodInfo *method) +{ + GString *ret; + char *method_name_uscored; + char *iface_prefix; + + iface_prefix = iface_to_c_prefix (interface_info_get_name (iface)); + ret = g_string_new (iface_prefix); + g_free (iface_prefix); + + method_name_uscored = _dbus_gutils_wincaps_to_uscore (method_info_get_name (method)); + g_string_append_c (ret, '_'); + g_string_append (ret, method_name_uscored); + g_free (method_name_uscored); + return g_string_free (ret, FALSE); +} + +static gboolean +write_formal_parameters (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, GError **error) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + const char *type_str; + int direction; + + arg = args->data; + + WRITE_OR_LOSE (", "); + + direction = arg_info_get_direction (arg); + + /* FIXME - broken for containers */ + type_str = dbus_gvalue_ctype_from_type (arg_info_get_type (arg), direction == ARG_IN); + + if (!type_str) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type %d to glib C type"), + arg_info_get_type (arg)); + return FALSE; + } + + switch (direction) + { + case ARG_IN: + if (!write_printf_to_iochannel ("%s IN_%s", channel, error, + type_str, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_OUT: + if (!write_printf_to_iochannel ("%s* OUT_%s", channel, error, + type_str, + arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_INVALID: + break; + } + } + + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +write_args_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, int direction, GError **error) +{ + GSList *args; + + for (args = method_info_get_args (method); args; args = args->next) + { + ArgInfo *arg; + const char *type_str; + + arg = args->data; + + if (direction != arg_info_get_direction (arg)) + continue; + + /* FIXME - broken for containers */ + type_str = dbus_gvalue_binding_type_from_type (arg_info_get_type (arg)); + if (!type_str) + { + g_set_error (error, + DBUS_BINDING_TOOL_ERROR, + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION, + _("Unsupported conversion from D-BUS type %c"), + (char) arg_info_get_type (arg)); + return FALSE; + } + + + switch (direction) + { + case ARG_IN: + if (!write_printf_to_iochannel (" %s, &IN_%s,\n", channel, error, + type_str, arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_OUT: + if (!write_printf_to_iochannel (" %s, OUT_%s,\n", channel, error, + type_str, arg_info_get_name (arg))) + goto io_lose; + break; + case ARG_INVALID: + break; + } + } + + return TRUE; + io_lose: + return FALSE; +} + +static gboolean +generate_client_glue_list (GSList *list, DBusBindingToolCData *data, GError **error) +{ + GSList *tmp; + + tmp = list; + while (tmp != NULL) + { + if (!generate_client_glue (tmp->data, data, error)) + return FALSE; + tmp = tmp->next; + } + return TRUE; +} + +static gboolean +generate_client_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error) +{ + if (base_info_get_type (base) == INFO_TYPE_NODE) + { + if (!generate_client_glue_list (node_info_get_nodes ((NodeInfo *) base), + data, error)) + return FALSE; + if (!generate_client_glue_list (node_info_get_interfaces ((NodeInfo *) base), + data, error)) + return FALSE; + } + else + { + GIOChannel *channel; + InterfaceInfo *interface; + GSList *methods; + GSList *tmp; + int count; + + channel = data->channel; + + interface = (InterfaceInfo *) base; + + methods = interface_info_get_methods (interface); + count = 0; + + for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp)) + { + MethodInfo *method; + char *method_name; + + method = (MethodInfo *) tmp->data; + + method_name = compute_client_method_name (interface, method); + + WRITE_OR_LOSE ("static gboolean\n"); + if (!write_printf_to_iochannel ("%s (DBusGProxy *proxy", channel, error, + method_name)) + goto io_lose; + g_free (method_name); + + if (!write_formal_parameters (interface, method, channel, error)) + goto io_lose; + + WRITE_OR_LOSE (", GError **error)\n\n"); + + WRITE_OR_LOSE ("{\n"); + WRITE_OR_LOSE (" gboolean ret;\n\n"); + WRITE_OR_LOSE (" DBusGPendingCall *call;\n\n"); + + if (!write_printf_to_iochannel (" call = dbus_g_proxy_begin_call (proxy, \"%s\",\n", + channel, error, + method_info_get_name (method))) + goto io_lose; + + if (!write_args_for_direction (interface, method, channel, ARG_IN, error)) + goto io_lose; + + WRITE_OR_LOSE (" DBUS_TYPE_INVALID);\n"); + WRITE_OR_LOSE (" ret = dbus_g_proxy_end_call (proxy, call, error,\n"); + + if (!write_args_for_direction (interface, method, channel, ARG_OUT, error)) + goto io_lose; + + WRITE_OR_LOSE (" DBUS_TYPE_INVALID);\n"); + + WRITE_OR_LOSE (" dbus_g_pending_call_unref (call);\n"); + WRITE_OR_LOSE (" return ret;\n"); + + WRITE_OR_LOSE ("}\n\n"); + } + } + return TRUE; + io_lose: + return FALSE; +} + + +gboolean +dbus_binding_tool_output_glib_client (BaseInfo *info, GIOChannel *channel, GError **error) +{ + DBusBindingToolCData data; + gboolean ret; + + memset (&data, 0, sizeof (data)); + + data.channel = channel; + + WRITE_OR_LOSE ("/* Generated by dbus-binding-tool; do not edit! */\n\n"); + WRITE_OR_LOSE ("#include <glib/gtypes.h>\n"); + WRITE_OR_LOSE ("#include <glib/gerror.h>\n"); + WRITE_OR_LOSE ("#include <dbus/dbus-glib.h>\n\n"); + + ret = generate_client_glue (info, &data, error); + + return ret; + io_lose: + return FALSE; +} diff --git a/glib/dbus-binding-tool-glib.h b/glib/dbus-binding-tool-glib.h new file mode 100644 index 00000000..d4589bc9 --- /dev/null +++ b/glib/dbus-binding-tool-glib.h @@ -0,0 +1,33 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-binding-tool-output-glib.h prototypes for glib output + * + * Copyright (C) 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_BINDING_TOOL_OUTPUT_GLIB_H +#define DBUS_BINDING_TOOL_OUTPUT_GLIB_H + +G_BEGIN_DECLS + +gboolean dbus_binding_tool_output_glib_client (BaseInfo *info, GIOChannel *channel, GError **error); +gboolean dbus_binding_tool_output_glib_server (BaseInfo *info, GIOChannel *channel, GError **error); + +G_END_DECLS + +#endif diff --git a/glib/dbus-gidl.c b/glib/dbus-gidl.c index ec87414d..554e18c3 100644 --- a/glib/dbus-gidl.c +++ b/glib/dbus-gidl.c @@ -43,6 +43,7 @@ struct NodeInfo struct InterfaceInfo { BaseInfo base; + GHashTable *bindings; /* Since we have BaseInfo now these could be one list */ GSList *methods; GSList *signals; @@ -52,6 +53,7 @@ struct InterfaceInfo struct MethodInfo { BaseInfo base; + GHashTable *bindings; GSList *args; }; @@ -75,6 +77,23 @@ struct ArgInfo ArgDirection direction; }; +static void +get_hash_key (gpointer key, gpointer value, gpointer data) +{ + GSList **list = data; + *list = g_slist_prepend (*list, key); +} + +static GSList * +get_hash_keys (GHashTable *table) +{ + GSList *ret = NULL; + + g_hash_table_foreach (table, get_hash_key, &ret); + + return ret; +} + BaseInfo * base_info_ref (BaseInfo *info) { @@ -326,6 +345,9 @@ interface_info_new (const char *name) info->base.refcount = 1; info->base.name = g_strdup (name); info->base.type = INFO_TYPE_INTERFACE; + info->bindings = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); return info; } @@ -344,6 +366,7 @@ interface_info_unref (InterfaceInfo *info) info->base.refcount -= 1; if (info->base.refcount == 0) { + g_hash_table_destroy (info->bindings); free_method_list (&info->methods); free_signal_list (&info->signals); free_property_list (&info->properties); @@ -357,6 +380,19 @@ interface_info_get_name (InterfaceInfo *info) return info->base.name; } +GSList * +interface_info_get_binding_names (InterfaceInfo *info) +{ + return get_hash_keys (info->bindings); +} + +const char* +interface_info_get_binding_name (InterfaceInfo *info, + const char *binding_type) +{ + return g_hash_table_lookup (info->bindings, binding_type); +} + GSList* interface_info_get_methods (InterfaceInfo *info) { @@ -376,6 +412,16 @@ interface_info_get_properties (InterfaceInfo *info) } void +interface_info_set_binding_name (InterfaceInfo *info, + const char *binding_type, + const char *bound_name) +{ + g_hash_table_insert (info->bindings, + g_strdup (binding_type), + g_strdup (bound_name)); +} + +void interface_info_add_method (InterfaceInfo *info, MethodInfo *method) { @@ -424,6 +470,9 @@ method_info_new (const char *name) info->base.refcount = 1; info->base.name = g_strdup (name); info->base.type = INFO_TYPE_METHOD; + info->bindings = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); return info; } @@ -442,6 +491,7 @@ method_info_unref (MethodInfo *info) info->base.refcount -= 1; if (info->base.refcount == 0) { + g_hash_table_destroy (info->bindings); free_arg_list (&info->args); base_info_free (info); } @@ -453,6 +503,19 @@ method_info_get_name (MethodInfo *info) return info->base.name; } +GSList * +method_info_get_binding_names (MethodInfo *info) +{ + return get_hash_keys (info->bindings); +} + +const char* +method_info_get_binding_name (MethodInfo *info, + const char *binding_type) +{ + return g_hash_table_lookup (info->bindings, binding_type); +} + GSList* method_info_get_args (MethodInfo *info) { @@ -481,6 +544,16 @@ args_sort_by_direction (const void *a, } void +method_info_set_binding_name (MethodInfo *info, + const char *binding_type, + const char *bound_name) +{ + g_hash_table_insert (info->bindings, + g_strdup (binding_type), + g_strdup (bound_name)); +} + +void method_info_add_arg (MethodInfo *info, ArgInfo *arg) { diff --git a/glib/dbus-gidl.h b/glib/dbus-gidl.h index a706c4d5..917432ee 100644 --- a/glib/dbus-gidl.h +++ b/glib/dbus-gidl.h @@ -90,19 +90,31 @@ InterfaceInfo* interface_info_new (const char *name); InterfaceInfo* interface_info_ref (InterfaceInfo *info); void interface_info_unref (InterfaceInfo *info); const char* interface_info_get_name (InterfaceInfo *info); +GSList* interface_info_get_binding_names(InterfaceInfo *info); +const char* interface_info_get_binding_name(InterfaceInfo*info, + const char *binding_type); GSList* interface_info_get_methods (InterfaceInfo *info); GSList* interface_info_get_signals (InterfaceInfo *info); GSList* interface_info_get_properties (InterfaceInfo *info); +void interface_info_set_binding_name(InterfaceInfo *info, + const char *name, + const char *value); void interface_info_add_method (InterfaceInfo *info, MethodInfo *method); void interface_info_add_signal (InterfaceInfo *info, SignalInfo *signal); void interface_info_add_property (InterfaceInfo *info, PropertyInfo *property); -MethodInfo* method_info_new (const char *name); +MethodInfo* method_info_new (const char *name); MethodInfo* method_info_ref (MethodInfo *info); void method_info_unref (MethodInfo *info); const char* method_info_get_name (MethodInfo *info); +GSList* method_info_get_binding_names (MethodInfo *info); +const char* method_info_get_binding_name (MethodInfo *info, + const char *binding_type); +void method_info_set_binding_name (MethodInfo *info, + const char *binding_type, + const char *bound_name); GSList* method_info_get_args (MethodInfo *info); void method_info_add_arg (MethodInfo *info, ArgInfo *arg); diff --git a/glib/dbus-glib-tool.c b/glib/dbus-glib-tool.c index c8273afa..022055e6 100644 --- a/glib/dbus-glib-tool.c +++ b/glib/dbus-glib-tool.c @@ -25,6 +25,7 @@ #include "dbus-gidl.h" #include "dbus-gparser.h" #include "dbus-gutils.h" +#include "dbus-binding-tool-glib.h" #include <locale.h> #include <libintl.h> #define _(x) dgettext (GETTEXT_PACKAGE, x) @@ -37,6 +38,13 @@ static void run_all_tests (const char *test_data_dir); #endif +typedef enum { + DBUS_BINDING_OUTPUT_NONE, + DBUS_BINDING_OUTPUT_PRETTY, + DBUS_BINDING_OUTPUT_GLIB_SERVER, + DBUS_BINDING_OUTPUT_GLIB_CLIENT, +} DBusBindingOutputMode; + static void indent (int depth) { @@ -99,11 +107,23 @@ pretty_print (BaseInfo *base, case INFO_TYPE_INTERFACE: { InterfaceInfo *i = (InterfaceInfo*) base; + GSList *binding_types, *elt; g_assert (name != NULL); printf (_("interface \"%s\" {\n"), name); + binding_types = interface_info_get_binding_names (i); + for (elt = binding_types; elt; elt = elt->next) + { + const char *binding_type = elt->data; + const char *binding_name = interface_info_get_binding_name (i, binding_type); + + printf (_(" (binding \"%s\": \"%s\") "), + binding_type, binding_name); + } + g_slist_free (binding_types); + pretty_print_list (interface_info_get_methods (i), depth + 1); pretty_print_list (interface_info_get_signals (i), depth + 1); pretty_print_list (interface_info_get_properties (i), depth + 1); @@ -115,10 +135,21 @@ pretty_print (BaseInfo *base, case INFO_TYPE_METHOD: { MethodInfo *m = (MethodInfo*) base; + GSList *binding_types, *elt; g_assert (name != NULL); - printf (_("method \"%s\" (\n"), name); + binding_types = method_info_get_binding_names (m); + printf (_("method \"%s\""), name); + for (elt = binding_types; elt; elt = elt->next) + { + const char *binding_type = elt->data; + const char *binding_name = method_info_get_binding_name (m, binding_type); + + printf (_(" (binding \"%s\": \"%s\") "), + binding_type, binding_name); + } + g_slist_free (binding_types); pretty_print_list (method_info_get_args (m), depth + 1); @@ -174,6 +205,16 @@ pretty_print (BaseInfo *base, } } +GQuark +dbus_binding_tool_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("dbus_binding_tool_error"); + + return quark; +} + static void usage (int ecode) { @@ -186,7 +227,7 @@ version (void) { printf ("D-BUS Binding Tool %s\n" "Copyright (C) 2003-2005 Red Hat, Inc.\n" - "This is free software; xsee the source for copying conditions.\n" + "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", VERSION); exit (0); @@ -198,16 +239,20 @@ main (int argc, char **argv) const char *prev_arg; int i; GSList *files; + DBusBindingOutputMode outputmode; gboolean end_of_args; GSList *tmp; - gboolean just_pretty_print; + GIOChannel *channel; + GError *error; setlocale (LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, DBUS_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); - just_pretty_print = FALSE; + g_type_init (); + + outputmode = DBUS_BINDING_OUTPUT_NONE; end_of_args = FALSE; files = NULL; prev_arg = NULL; @@ -228,8 +273,18 @@ main (int argc, char **argv) else if (strcmp (arg, "--self-test") == 0) run_all_tests (NULL); #endif /* DBUS_BUILD_TESTS */ - else if (strcmp (arg, "--pretty-print") == 0) - just_pretty_print = TRUE; + else if (strncmp (arg, "--mode=", 7) == 0) + { + const char *mode = arg + 7; + if (!strcmp (mode, "pretty")) + outputmode = DBUS_BINDING_OUTPUT_PRETTY; + else if (!strcmp (mode, "glib-server")) + outputmode = DBUS_BINDING_OUTPUT_GLIB_SERVER; + else if (!strcmp (mode, "glib-client")) + outputmode = DBUS_BINDING_OUTPUT_GLIB_CLIENT; + else + usage (1); + } else if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') @@ -251,6 +306,15 @@ main (int argc, char **argv) ++i; } + error = NULL; + channel = g_io_channel_unix_new (fileno (stdout)); + if (!g_io_channel_set_encoding (channel, NULL, &error)) + { + fprintf (stderr, _("Couldn't set channel encoding to NULL: %s\n"), + error->message); + exit (1); + } + files = g_slist_reverse (files); tmp = files; @@ -273,27 +337,43 @@ main (int argc, char **argv) g_error_free (error); exit (1); } - else if (just_pretty_print) - { - pretty_print ((BaseInfo*) node, 0); - } else - { - /* FIXME process the file to generate metadata variable - * definition rather than just printing it. - * i.e. we want to create DBusGObjectInfo. - * This probably requires extending the introspection XML format to - * allow a "native function name": - * <method name="Frobate" native="my_object_frobate"> - */ - pretty_print ((BaseInfo*) node, 0); - } + { + switch (outputmode) + { + case DBUS_BINDING_OUTPUT_PRETTY: + pretty_print ((BaseInfo*) node, 0); + break; + case DBUS_BINDING_OUTPUT_GLIB_SERVER: + if (!dbus_binding_tool_output_glib_server ((BaseInfo *) node, channel, &error)) + { + g_error (_("Compilation failed: %s\n"), error->message); + exit (1); + } + break; + case DBUS_BINDING_OUTPUT_GLIB_CLIENT: + if (!dbus_binding_tool_output_glib_client ((BaseInfo *) node, channel, &error)) + { + g_error (_("Compilation failed: %s\n"), error->message); + exit (1); + } + break; + case DBUS_BINDING_OUTPUT_NONE: + break; + } + } if (node) node_info_unref (node); tmp = tmp->next; } + + if (!g_io_channel_flush (channel, &error)) + { + g_error (_("Failed to flush IO channel: %s"), error->message); + exit (1); + } return 0; } diff --git a/glib/dbus-glib-tool.h b/glib/dbus-glib-tool.h new file mode 100644 index 00000000..b56d764a --- /dev/null +++ b/glib/dbus-glib-tool.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-glib-tool.h: Definitions used internally by binding tool + * + * Copyright (C) 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_BINDING_TOOL_H +#define DBUS_BINDING_TOOL_H + +#include <glib/gquark.h> + +typedef enum +{ + DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION +} DBusBindingToolError; + +#define DBUS_BINDING_TOOL_ERROR dbus_binding_tool_error_quark () + +GQuark dbus_binding_tool_error_quark (void); + +#endif diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c index abaf2e0e..3bcd90d5 100644 --- a/glib/dbus-gobject.c +++ b/glib/dbus-gobject.c @@ -1,7 +1,7 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-gobject.c Exporting a GObject remotely * - * Copyright (C) 2003, 2004 Red Hat, Inc. + * Copyright (C) 2003, 2004, 2005 Red Hat, Inc. * * Licensed under the Academic Free License version 2.1 * @@ -26,6 +26,7 @@ #include <dbus/dbus-glib-lowlevel.h> #include "dbus-gtest.h" #include "dbus-gutils.h" +#include "dbus-gobject.h" #include "dbus-gvalue.h" #include <string.h> @@ -34,37 +35,10 @@ * @{ */ -static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT; +static GStaticRWLock info_hash_lock = G_STATIC_RW_LOCK_INIT; static GHashTable *info_hash = NULL; static char* -wincaps_to_uscore (const char *caps) -{ - const char *p; - GString *str; - - str = g_string_new (NULL); - p = caps; - while (*p) - { - if (g_ascii_isupper (*p)) - { - if (str->len > 0 && - (str->len < 2 || str->str[str->len-2] != '_')) - g_string_append_c (str, '_'); - g_string_append_c (str, g_ascii_tolower (*p)); - } - else - { - g_string_append_c (str, *p); - } - ++p; - } - - return g_string_free (str, FALSE); -} - -static char* uscore_to_wincaps (const char *uscore) { const char *p; @@ -97,6 +71,164 @@ uscore_to_wincaps (const char *uscore) return g_string_free (str, FALSE); } +static const char * +string_table_next (const char *table) +{ + return (table + (strlen (table) + 1)); +} + +static const char * +string_table_lookup (const char *table, int index) +{ + const char *ret; + + ret = table; + + while (index--) + ret = string_table_next (ret); + + return ret; +} + +static const char * +get_method_data (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return object->data + method->data_offset; +} + +static char * +object_error_domain_prefix_from_object_info (const DBusGObjectInfo *info) +{ + /* FIXME */ + return NULL; +} + +static char * +object_error_code_from_object_info (const DBusGObjectInfo *info, GQuark domain, gint code) +{ + /* FIXME */ + return NULL; +} + +static const char * +method_interface_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return string_table_lookup (get_method_data (object, method), 0); +} + +static const char * +method_name_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return string_table_lookup (get_method_data (object, method), 1); +} + +static const char * +method_arg_info_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return string_table_lookup (get_method_data (object, method), 2); +} + +static const char * +arg_iterate (const char *data, const char **name, gboolean *in, + const char **type) +{ + *name = data; + + data = string_table_next (data); + switch (*data) + { + case 'I': + *in = TRUE; + break; + case 'O': + *in = FALSE; + break; + default: + g_warning ("invalid arg direction"); + break; + } + + data = string_table_next (data); + *type = data; + + return string_table_next (data); +} + +static char * +method_dir_signature_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method, + gboolean in) +{ + const char *arg; + GString *ret; + + arg = method_arg_info_from_object_info (object, method); + + ret = g_string_new (NULL); + + while (*arg) + { + const char *name; + gboolean arg_in; + const char *type; + + arg = arg_iterate (arg, &name, &arg_in, &type); + + if (arg_in == in) + g_string_append (ret, type); + } + + return g_string_free (ret, FALSE); +} + +static char * +method_input_signature_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return method_dir_signature_from_object_info (object, method, TRUE); +} + +static char * +method_output_signature_from_object_info (const DBusGObjectInfo *object, + const DBusGMethodInfo *method) +{ + return method_dir_signature_from_object_info (object, method, FALSE); +} + +GValueArray * +dbus_glib_marshal_dbus_message_to_gvalue_array (DBusMessage *message) +{ + GValueArray *ret; + DBusMessageIter iter; + int dtype; + + ret = g_value_array_new (6); /* 6 is a typical maximum for arguments */ + dbus_message_iter_init (message, &iter); + + while ((dtype = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) + { + GValue value = { 0, }; + + if (!dbus_gvalue_demarshal (&iter, &value)) + { + g_warning ("Unable to convert arg type %d to GValue", dtype); + g_value_array_free (ret); + ret = NULL; + goto out; + } + g_value_array_append (ret, &value); + + dbus_message_iter_next (&iter); + } + + out: + return ret; +} + static void gobject_unregister_function (DBusConnection *connection, void *user_data) @@ -272,6 +404,115 @@ introspect_signals (GType type, GString *xml) g_string_append (xml, " </interface>\n"); } +typedef struct +{ + GString *xml; + const DBusGObjectInfo *object_info; +} DBusGlibWriteIterfaceData; + +static void +write_interface (gpointer key, gpointer val, gpointer user_data) +{ + const char *name; + GSList *methods; + GString *xml; + const DBusGObjectInfo *object_info; + DBusGlibWriteIterfaceData *data; + + name = key; + methods = val; + data = user_data; + xml = data->xml; + object_info = data->object_info; + + g_string_append_printf (xml, " <interface name=\"%s\">\n", name); + + /* FIXME: recurse to parent types ? */ + for (; methods; methods = methods->next) + { + DBusGMethodInfo *method; + method = methods->data; + const char *args; + + g_string_append_printf (xml, " <method name=\"%s\">\n", + method_name_from_object_info (object_info, method)); + + args = method_arg_info_from_object_info (object_info, method); + + while (*args) + { + const char *name; + gboolean arg_in; + const char *type; + + args = arg_iterate (args, &name, &arg_in, &type); + + /* FIXME - handle container types */ + g_string_append_printf (xml, " <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n", + name, _dbus_gutils_type_to_string (type[0]), arg_in ? "in" : "out"); + + } + g_string_append (xml, " </method>\n"); + } + + g_string_append (xml, " </interface>\n"); +} + +static void +introspect_interfaces (GObject *object, GString *xml) +{ + GType classtype; + + g_static_rw_lock_reader_lock (&info_hash_lock); + + for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype)) + { + const DBusGObjectInfo *info; + DBusGlibWriteIterfaceData data; + + info = g_hash_table_lookup (info_hash, + g_type_class_peek (classtype)); + + if (info != NULL && info->format_version == 0) + { + int i; + GHashTable *interfaces; + + /* Gather a list of all interfaces, indexed into their methods */ + interfaces = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < info->n_infos; i++) + { + const char *method_name; + const char *method_interface; + const char *method_args; + const DBusGMethodInfo *method; + GSList *methods; + + method = &(info->infos[i]); + + method_interface = method_interface_from_object_info (info, method); + method_name = method_name_from_object_info (info, method); + method_args = method_arg_info_from_object_info (info, method); + + if ((methods = g_hash_table_lookup (interfaces, method_interface)) == NULL) + methods = g_slist_prepend (NULL, (gpointer) method); + else + methods = g_slist_prepend (methods, (gpointer) method); + g_hash_table_insert (interfaces, (gpointer) method_interface, methods); + } + + memset (&data, 0, sizeof (data)); + data.xml = xml; + data.object_info = info; + g_hash_table_foreach (interfaces, write_interface, &data); + + g_hash_table_destroy (interfaces); + } + } + + g_static_rw_lock_reader_lock (&info_hash_lock); +} + static DBusHandlerResult handle_introspect (DBusConnection *connection, DBusMessage *message, @@ -316,6 +557,7 @@ handle_introspect (DBusConnection *connection, introspect_signals (G_OBJECT_TYPE (object), xml); introspect_properties (object, xml); + introspect_interfaces (object, xml); /* Append child nodes */ for (i = 0; children[i]; i++) @@ -392,7 +634,7 @@ get_object_property (DBusConnection *connection, GParamSpec *pspec) { GType value_type; - GValue value; + GValue value = {0, }; DBusMessage *ret; DBusMessageIter iter; @@ -420,12 +662,275 @@ get_object_property (DBusConnection *connection, return ret; } +static gboolean +lookup_object_and_method (GObject *object, + DBusMessage *message, + const DBusGObjectInfo **object_ret, + const DBusGMethodInfo **method_ret) +{ + GType classtype; + const char *interface; + const char *member; + const char *signature; + gboolean ret; + + interface = dbus_message_get_interface (message); + member = dbus_message_get_member (message); + signature = dbus_message_get_signature (message); + ret = FALSE; + + g_static_rw_lock_reader_lock (&info_hash_lock); + + if (!info_hash) + goto out; + + for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype)) + { + const DBusGObjectInfo *info; + + info = g_hash_table_lookup (info_hash, + g_type_class_peek (classtype)); + + *object_ret = info; + + if (info != NULL && info->format_version == 0) + { + int i; + for (i = 0; i < info->n_infos; i++) + { + const char *expected_member; + const char *expected_interface; + char *expected_signature; + const DBusGMethodInfo *method; + + method = &(info->infos[i]); + + /* Check method interface/name and input signature */ + expected_interface = method_interface_from_object_info (*object_ret, method); + expected_member = method_name_from_object_info (*object_ret, method); + expected_signature = method_input_signature_from_object_info (*object_ret, method); + if ((interface == NULL + || strcmp (expected_interface, interface) == 0) + && strcmp (expected_member, member) == 0 + && strcmp (expected_signature, signature) == 0) + { + g_free (expected_signature); + *method_ret = method; + ret = TRUE; + goto out; + } + g_free (expected_signature); + } + } + } + out: + g_static_rw_lock_reader_lock (&info_hash_lock); + return ret; +} + +static char * +gerror_domaincode_to_dbus_error_name (const DBusGObjectInfo *object_info, + GQuark domain, gint code) +{ + const char *domain_str; + const char *code_str; + GString *dbus_error_name; + + domain_str = object_error_domain_prefix_from_object_info (object_info); + code_str = object_error_code_from_object_info (object_info, domain, code); + + if (!domain_str || !code_str) + { + /* If we can't map it sensibly, make up an error name */ + char *domain_from_quark; + + dbus_error_name = g_string_new ("org.freedesktop.DBus.GLib.UnmappedError."); + + domain_from_quark = uscore_to_wincaps (g_quark_to_string (domain)); + g_string_append (dbus_error_name, domain_from_quark); + g_free (domain_from_quark); + + g_string_append_printf (dbus_error_name, ".Code%d", code); + } + else + { + dbus_error_name = g_string_new (domain_str); + g_string_append_c (dbus_error_name, '.'); + g_string_append (dbus_error_name, code_str); + } + + return g_string_free (dbus_error_name, FALSE); +} + +static DBusMessage * +gerror_to_dbus_error_message (const DBusGObjectInfo *object_info, + DBusMessage *message, + GError *error) +{ + DBusMessage *reply; + + if (!error) + { + char *error_msg; + + error_msg = g_strdup_printf ("Method invoked for %s returned FALSE but did not set error", dbus_message_get_member (message)); + reply = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg); + g_free (error_msg); + } + else + { + char *error_name; + error_name = gerror_domaincode_to_dbus_error_name (object_info, error->domain, error->code); + reply = dbus_message_new_error (message, error_name, error->message); + g_free (error_name); + } + return reply; +} + +static DBusHandlerResult +invoke_object_method (GObject *object, + const DBusGObjectInfo *object_info, + const DBusGMethodInfo *method, + DBusConnection *connection, + DBusMessage *message) +{ + gboolean had_error; + GError *gerror; + GValueArray *value_array; + GValue object_value = {0,}; + GValue error_value = {0,}; + GValue return_value = {0,}; + GClosure closure; + char *out_signature; + int out_signature_len; + GArray *out_param_values; + int i; + DBusHandlerResult result; + DBusMessage *reply; + + gerror = NULL; + + /* This is evil. We do this to work around the fact that + * the generated glib marshallers check a flag in the closure object + * which we don't care about. We don't need/want to create + * a new closure for each invocation. + */ + memset (&closure, 0, sizeof (closure)); + + /* Convert method IN parameters to GValueArray */ + value_array = dbus_glib_marshal_dbus_message_to_gvalue_array (message); + + g_return_val_if_fail (value_array != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); + + /* Prepend object as first argument */ + g_value_init (&object_value, G_TYPE_OBJECT); + g_value_set_object (&object_value, object); + g_value_array_prepend (value_array, &object_value); + + out_signature = method_output_signature_from_object_info (object_info, method); + out_signature_len = strlen (out_signature); + + /* Create an array to store the actual values of OUT + * parameters. Then, create a GValue boxed POINTER + * to each of those values, and append to the invocation, + * so the method can return the OUT parameters. + */ + out_param_values = g_array_new (FALSE, TRUE, sizeof (DBusBasicGValue)); + for (i = 0; i < out_signature_len; i++) + { + GValue value = {0, }; + DBusBasicGValue basic; + + memset (&basic, 0, sizeof (basic)); + + /* FIXME - broken for container types */ + + g_array_append_val (out_param_values, basic); + g_value_init (&value, G_TYPE_POINTER); + g_value_set_pointer (&value, &(g_array_index (out_param_values, DBusBasicGValue, i))); + g_value_array_append (value_array, &value); + } + + /* Append GError as final argument */ + g_value_init (&error_value, G_TYPE_POINTER); + g_value_set_pointer (&error_value, &gerror); + g_value_array_append (value_array, &error_value); + + /* Actually invoke method */ + g_value_init (&return_value, G_TYPE_BOOLEAN); + method->marshaller (&closure, &return_value, + value_array->n_values, + value_array->values, + NULL, method->function); + had_error = !g_value_get_boolean (&return_value); + + if (!had_error) + { + DBusMessageIter iter; + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + goto nomem; + + /* Append OUT arguments to reply */ + dbus_message_iter_init_append (reply, &iter); + for (i = 0; i < out_signature_len; i++) + { + DBusBasicGValue *value; + + /* FIXME - broken for container types */ + + value = &(g_array_index (out_param_values, DBusBasicGValue, i)); + if (!dbus_message_iter_append_basic (&iter, out_signature[i], value)) + goto nomem; + + } + } + else + reply = gerror_to_dbus_error_message (object_info, message, gerror); + + if (reply) + { + dbus_connection_send (connection, reply, NULL); + dbus_message_unref (reply); + } + + /* Assume that if there was an error, no return values are + * set */ + if (!had_error) + { + /* Be sure to free all returned STRING arguments for now; + * later this should be specified via method info parameter + * annotation; probably we want to support custom free funcs too */ + for (i = 0; i < out_signature_len; i++) + { + DBusBasicGValue *value; + + value = &(g_array_index (out_param_values, DBusBasicGValue, i)); + if (out_signature[i] == DBUS_TYPE_STRING) + g_free (value->gpointer_val); + } + } + + result = DBUS_HANDLER_RESULT_HANDLED; + done: + g_free (out_signature); + g_array_free (out_param_values, TRUE); + g_value_array_free (value_array); + g_value_unset (&object_value); + g_value_unset (&error_value); + g_value_unset (&return_value); + return result; + nomem: + result = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto done; +} + static DBusHandlerResult gobject_message_function (DBusConnection *connection, DBusMessage *message, void *user_data) { - const DBusGObjectInfo *info; GParamSpec *pspec; GObject *object; gboolean setter; @@ -434,6 +939,8 @@ gobject_message_function (DBusConnection *connection, const char *wincaps_propname; /* const char *wincaps_propiface; */ DBusMessageIter iter; + const DBusGMethodInfo *method; + const DBusGObjectInfo *object_info; object = G_OBJECT (user_data); @@ -441,23 +948,10 @@ gobject_message_function (DBusConnection *connection, DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE, "Introspect")) return handle_introspect (connection, message, object); - + /* Try the metainfo, which lets us invoke methods */ - - g_static_mutex_lock (&info_hash_mutex); - /* FIXME this needs to walk up the inheritance tree, not - * just look at the most-derived class - */ - info = g_hash_table_lookup (info_hash, - G_OBJECT_GET_CLASS (object)); - g_static_mutex_unlock (&info_hash_mutex); - - if (info != NULL) - { - - - - } + if (lookup_object_and_method (object, message, &object_info, &method)) + return invoke_object_method (object, object_info, method, connection, message); /* If no metainfo, we can still do properties and signals * via standard GLib introspection @@ -497,7 +991,7 @@ gobject_message_function (DBusConnection *connection, dbus_message_iter_get_basic (&iter, &wincaps_propname); dbus_message_iter_next (&iter); - s = wincaps_to_uscore (wincaps_propname); + s = _dbus_gutils_wincaps_to_uscore (wincaps_propname); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), s); @@ -577,7 +1071,7 @@ dbus_g_object_class_install_info (GObjectClass *object_class, { g_return_if_fail (G_IS_OBJECT_CLASS (object_class)); - g_static_mutex_lock (&info_hash_mutex); + g_static_rw_lock_writer_lock (&info_hash_lock); if (info_hash == NULL) { @@ -586,7 +1080,7 @@ dbus_g_object_class_install_info (GObjectClass *object_class, g_hash_table_replace (info_hash, object_class, (void*) info); - g_static_mutex_unlock (&info_hash_mutex); + g_static_rw_lock_writer_unlock (&info_hash_lock); } /** @@ -652,7 +1146,7 @@ _dbus_gobject_test (const char *test_data_dir) char *uscore; char *wincaps; - uscore = wincaps_to_uscore (name_pairs[i].wincaps); + uscore = _dbus_gutils_wincaps_to_uscore (name_pairs[i].wincaps); wincaps = uscore_to_wincaps (name_pairs[i].uscore); if (strcmp (uscore, name_pairs[i].uscore) != 0) diff --git a/glib/dbus-gobject.h b/glib/dbus-gobject.h new file mode 100644 index 00000000..28bba5c3 --- /dev/null +++ b/glib/dbus-gobject.h @@ -0,0 +1,35 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gobject.h: common functions used to map between D-BUS and GObject + * + * Copyright (C) 2005 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_GLIB_OBJECT_H +#define DBUS_GLIB_OBJECT_H + +#include <dbus/dbus.h> +#include <glib.h> + +G_BEGIN_DECLS + +GValueArray* dbus_glib_marshal_dbus_message_to_gvalue_array (DBusMessage *message); + +G_END_DECLS + +#endif diff --git a/glib/dbus-gparser.c b/glib/dbus-gparser.c index 680d830a..05136aa8 100644 --- a/glib/dbus-gparser.c +++ b/glib/dbus-gparser.c @@ -308,6 +308,7 @@ parse_interface (Parser *parser, GError **error) { const char *name; + const char *c_name; InterfaceInfo *iface; NodeInfo *top; @@ -329,6 +330,7 @@ parse_interface (Parser *parser, if (!locate_attributes (element_name, attribute_names, attribute_values, error, "name", &name, + "c_name", &c_name, NULL)) return FALSE; @@ -344,6 +346,8 @@ parse_interface (Parser *parser, top = parser->node_stack->data; iface = interface_info_new (name); + if (c_name) + interface_info_set_binding_name (iface, "C", c_name); node_info_add_interface (top, iface); interface_info_unref (iface); @@ -360,6 +364,7 @@ parse_method (Parser *parser, GError **error) { const char *name; + const char *c_name; MethodInfo *method; NodeInfo *top; @@ -381,6 +386,7 @@ parse_method (Parser *parser, if (!locate_attributes (element_name, attribute_names, attribute_values, error, "name", &name, + "c_name", &c_name, NULL)) return FALSE; @@ -396,6 +402,8 @@ parse_method (Parser *parser, top = parser->node_stack->data; method = method_info_new (name); + if (c_name) + method_info_set_binding_name (method, "C", c_name); interface_info_add_method (parser->interface, method); method_info_unref (method); diff --git a/glib/dbus-gproxy.c b/glib/dbus-gproxy.c index 17b76d93..364facf8 100644 --- a/glib/dbus-gproxy.c +++ b/glib/dbus-gproxy.c @@ -25,6 +25,7 @@ #include "dbus-gutils.h" #include "dbus-gmarshal.h" #include "dbus-gvalue.h" +#include "dbus-gobject.h" #include <string.h> /** @@ -900,11 +901,8 @@ marshal_dbus_message_to_g_marshaller (GClosure *closure, * marshaller. */ #define MAX_SIGNATURE_ARGS 20 - GValue expanded[MAX_SIGNATURE_ARGS]; - int arg; - int i; - DBusMessageIter iter; - int dtype; + GValueArray *value_array; + GValue value = {0, }; GSignalCMarshaller c_marshaller; DBusGProxy *proxy; DBusMessage *message; @@ -924,44 +922,18 @@ marshal_dbus_message_to_g_marshaller (GClosure *closure, g_return_if_fail (c_marshaller != NULL); - memset (&expanded[0], 0, sizeof (expanded)); - - arg = 0; - - g_value_init (&expanded[arg], G_TYPE_FROM_INSTANCE (proxy)); - g_value_set_instance (&expanded[arg], proxy); - ++arg; - - dbus_message_iter_init (message, &iter); + value_array = dbus_glib_marshal_dbus_message_to_gvalue_array (message); + + g_return_if_fail (value_array != NULL); - while ((dtype = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) - { - if (arg == MAX_SIGNATURE_ARGS) - { - g_warning ("Don't support more than %d signal args\n", MAX_SIGNATURE_ARGS); - goto out; - } - - if (!dbus_gvalue_demarshal (&iter, &expanded[arg])) - { - g_warning ("Unable to convert arg type %d to GValue to emit DBusGProxy signal", dtype); - goto out; - } - - ++arg; - dbus_message_iter_next (&iter); - } + g_value_init (&value, G_TYPE_FROM_INSTANCE (proxy)); + g_value_set_instance (&value, proxy); + g_value_array_prepend (value_array, &value); - (* c_marshaller) (closure, return_value, arg, &expanded[0], - invocation_hint, marshal_data); + (* c_marshaller) (closure, return_value, value_array->n_values, + value_array->values, invocation_hint, marshal_data); - out: - i = 0; - while (i < arg) - { - g_value_unset (&expanded[i]); - ++i; - } + g_value_array_free (value_array); } static void diff --git a/glib/dbus-gutils.c b/glib/dbus-gutils.c index cf12245c..30d00fed 100644 --- a/glib/dbus-gutils.c +++ b/glib/dbus-gutils.c @@ -125,6 +125,34 @@ _dbus_gutils_type_to_string (int type) } } +char* +_dbus_gutils_wincaps_to_uscore (const char *caps) +{ + const char *p; + GString *str; + + str = g_string_new (NULL); + p = caps; + while (*p) + { + if (g_ascii_isupper (*p)) + { + if (str->len > 0 && + (str->len < 2 || str->str[str->len-2] != '_')) + g_string_append_c (str, '_'); + g_string_append_c (str, g_ascii_tolower (*p)); + } + else + { + g_string_append_c (str, *p); + } + ++p; + } + + return g_string_free (str, FALSE); +} + + #ifdef DBUS_BUILD_TESTS /** diff --git a/glib/dbus-gutils.h b/glib/dbus-gutils.h index fd2cac88..0356b30e 100644 --- a/glib/dbus-gutils.h +++ b/glib/dbus-gutils.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS char **_dbus_gutils_split_path (const char *path); const char *_dbus_gutils_type_to_string (int type); +char *_dbus_gutils_wincaps_to_uscore (const char *uscore); /* These munge the pointer to enforce that a plain cast won't work, * accessor functions must be used; i.e. to ensure the ABI diff --git a/glib/dbus-gvalue.c b/glib/dbus-gvalue.c index e2bf40b2..8306be9b 100644 --- a/glib/dbus-gvalue.c +++ b/glib/dbus-gvalue.c @@ -2,6 +2,7 @@ /* dbus-gvalue.c GValue to-from DBusMessageIter * * Copyright (C) 2004 Ximian, Inc. + * Copyright (C) 2005 Red Hat, Inc. * * Licensed under the Academic Free License version 2.1 * @@ -23,20 +24,178 @@ #include <dbus-gvalue.h> +/* This is slightly evil, we don't use g_value_set_foo() functions */ +#define MAP_BASIC_INIT(d_t, g_t) \ + case DBUS_TYPE_##d_t: \ + g_value_init (value, G_TYPE_##g_t); \ + break + +gboolean +dbus_gvalue_init (int type, + GValue *value) +{ + gboolean can_convert; + + can_convert = TRUE; + + switch (type) + { + MAP_BASIC_INIT (BOOLEAN, BOOLEAN); + MAP_BASIC_INIT (BYTE, UCHAR); + MAP_BASIC_INIT (INT32, INT); + MAP_BASIC_INIT (UINT32, UINT); + MAP_BASIC_INIT (INT64, INT64); + MAP_BASIC_INIT (UINT64, UINT64); + MAP_BASIC_INIT (DOUBLE, DOUBLE); + + case DBUS_TYPE_INT16: + g_value_init (value, G_TYPE_INT); + break; + case DBUS_TYPE_UINT16: + g_value_init (value, G_TYPE_UINT); + break; + + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + g_value_init (value, G_TYPE_STRING); + break; + + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_VARIANT: + default: + can_convert = FALSE; + } +#undef MAP_BASIC_INIT + return can_convert; +} + +const char * +dbus_gvalue_genmarshal_name_from_type (int type) +{ + switch (type) + { + case DBUS_TYPE_BOOLEAN: + return "BOOLEAN"; + case DBUS_TYPE_BYTE: + return "UCHAR"; + case DBUS_TYPE_INT32: + return "INT"; + case DBUS_TYPE_UINT32: + return "UINT"; + case DBUS_TYPE_INT64: + return "INT64"; + case DBUS_TYPE_UINT64: + return "UINT64"; + case DBUS_TYPE_DOUBLE: + return "DOUBLE"; + case DBUS_TYPE_INT16: + return "INT"; + break; + case DBUS_TYPE_UINT16: + return "UINT"; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return "STRING"; + + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_VARIANT: + return NULL; + } + return NULL; +} + +const char * +dbus_gvalue_binding_type_from_type (int type) +{ +#define STRINGIFY(x) \ + case x: \ + return (#x) + + switch (type) + { + STRINGIFY(DBUS_TYPE_BOOLEAN); + STRINGIFY(DBUS_TYPE_BYTE); + STRINGIFY(DBUS_TYPE_INT32); + STRINGIFY(DBUS_TYPE_UINT32); + STRINGIFY(DBUS_TYPE_INT64); + STRINGIFY(DBUS_TYPE_UINT64); + STRINGIFY(DBUS_TYPE_DOUBLE); + case DBUS_TYPE_INT16: + return "DBUS_TYPE_INT32"; + case DBUS_TYPE_UINT16: + return "DBUS_TYPE_UINT32"; + STRINGIFY(DBUS_TYPE_STRING); + STRINGIFY(DBUS_TYPE_OBJECT_PATH); + STRINGIFY(DBUS_TYPE_SIGNATURE); + + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_VARIANT: + return NULL; + } +#undef STRINGIFY + return NULL; +} + +const char * +dbus_gvalue_ctype_from_type (int type, gboolean in) +{ + switch (type) + { + case DBUS_TYPE_BOOLEAN: + return "gboolean"; + case DBUS_TYPE_BYTE: + return "guchar"; + case DBUS_TYPE_INT32: + return "gint32"; + case DBUS_TYPE_UINT32: + return "guint32"; + case DBUS_TYPE_INT64: + return "gint64"; + case DBUS_TYPE_UINT64: + return "guint64"; + case DBUS_TYPE_DOUBLE: + return "gdouble"; + case DBUS_TYPE_INT16: + return "gint"; + break; + case DBUS_TYPE_UINT16: + return "guint"; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + /* FIXME - kind of a hack */ + if (in) + return "const char *"; + else + return "char *"; + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_ARRAY: + case DBUS_TYPE_VARIANT: + return NULL; + } + return NULL; +} + gboolean dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value) { gboolean can_convert = TRUE; - /* This is slightly evil, we don't use g_value_set_foo() functions */ + g_assert (sizeof (dbus_bool_t) == sizeof (value->data[0].v_int)); + + dbus_gvalue_init (dbus_message_iter_get_arg_type (iter), value); + +/* This is slightly evil, we don't use g_value_set_foo() functions */ #define MAP_BASIC(d_t, g_t) \ case DBUS_TYPE_##d_t: \ - g_value_init (value, G_TYPE_##g_t); \ dbus_message_iter_get_basic (iter, &value->data[0]); \ break - g_assert (sizeof (dbus_bool_t) == sizeof (value->data[0].v_int)); - switch (dbus_message_iter_get_arg_type (iter)) { MAP_BASIC (BOOLEAN, BOOLEAN); @@ -50,7 +209,6 @@ dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value) case DBUS_TYPE_INT16: { dbus_int16_t v; - g_value_init (value, G_TYPE_INT); dbus_message_iter_get_basic (iter, &v); g_value_set_int (value, v); } @@ -58,7 +216,6 @@ dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value) case DBUS_TYPE_UINT16: { dbus_uint16_t v; - g_value_init (value, G_TYPE_UINT); dbus_message_iter_get_basic (iter, &v); g_value_set_uint (value, v); } @@ -70,8 +227,6 @@ dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value) { const char *s; - g_value_init (value, G_TYPE_STRING); - dbus_message_iter_get_basic (iter, &s); g_value_set_string (value, s); } diff --git a/glib/dbus-gvalue.h b/glib/dbus-gvalue.h index fc45f7e0..fafda585 100644 --- a/glib/dbus-gvalue.h +++ b/glib/dbus-gvalue.h @@ -7,8 +7,32 @@ G_BEGIN_DECLS -gboolean dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value); -gboolean dbus_gvalue_marshal (DBusMessageIter *iter, GValue *value); +/* Used for return value storage */ +typedef union +{ + gboolean gboolean_val; + guchar guchar_val; + gint int_val; + gint64 gint64_val; + guint64 guint64_val; + double double_val; + gpointer gpointer_val; + char * chararray_val; +} DBusBasicGValue; + +const char * dbus_gvalue_genmarshal_name_from_type (int type); + +const char * dbus_gvalue_ctype_from_type (int type, gboolean in); + +const char * dbus_gvalue_binding_type_from_type (int type); + +gboolean dbus_gvalue_init (int type, + GValue *value); + +gboolean dbus_gvalue_demarshal (DBusMessageIter *iter, + GValue *value); +gboolean dbus_gvalue_marshal (DBusMessageIter *iter, + GValue *value); G_END_DECLS |