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.
This commit is contained in:
Colin Walters 2005-02-17 17:41:30 +00:00
parent 9e4450872a
commit 03f6615eac
21 changed files with 2348 additions and 135 deletions

129
ChangeLog
View file

@ -1,3 +1,132 @@
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.
2005-02-15 Havoc Pennington <hp@redhat.com>
* dbus/dbus-connection.c (dbus_connection_dispatch): always

View file

@ -89,25 +89,21 @@ DBusGConnection* dbus_g_bus_get (DBusBusType type,
typedef struct DBusGObjectInfo DBusGObjectInfo;
typedef struct DBusGMethodInfo DBusGMethodInfo;
typedef DBusHandlerResult (* DBusGMethodMarshaller) (DBusGConnection *connection,
DBusGMessage *message,
void *user_data);
/**
* Object typically generated by dbus-glib-tool that
* Object typically generated by dbus-binding-tool that
* stores a mapping from introspection data to a
* function pointer for a C method to be invoked.
*/
struct DBusGMethodInfo
{
GCallback function; /**< C method to invoke */
DBusGMethodMarshaller marshaller; /**< Marshaller to go DBusGMessage to C method */
GClosureMarshal marshaller; /**< Marshaller to invoke method */
int data_offset; /**< Offset into the introspection data */
};
/**
* Introspection data for a GObject, normally autogenerated by
* a tool such as dbus-glib-tool.
* a tool such as dbus-binding-tool.
*/
struct DBusGObjectInfo
{
@ -115,7 +111,8 @@ struct DBusGObjectInfo
* by adding DBusGObjectInfo2, DBusGObjectInfo3, etc.
*/
const DBusGMethodInfo *infos; /**< Array of method pointers */
const unsigned char *data; /**< Introspection data */
int n_infos; /**< Length of the infos array */
const char *data; /**< Introspection data */
};
void dbus_g_object_class_install_info (GObjectClass *object_class,
@ -174,6 +171,7 @@ void dbus_g_proxy_call_no_reply (DBusGProxy *proxy,
const char *method,
int first_arg_type,
...);
const char* dbus_g_proxy_get_bus_name (DBusGProxy *proxy);
#undef DBUS_INSIDE_DBUS_GLIB_H

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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)
{
@ -375,6 +411,16 @@ interface_info_get_properties (InterfaceInfo *info)
return info->properties;
}
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)
{
@ -480,6 +543,16 @@ args_sort_by_direction (const void *a,
return 1;
}
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)

View file

@ -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);

View file

@ -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;
}

37
glib/dbus-glib-tool.h Normal file
View file

@ -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

View file

@ -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,36 +35,9 @@
* @{
*/
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)
{
@ -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)

35
glib/dbus-gobject.h Normal file
View file

@ -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

View file

@ -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);

View file

@ -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);
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);
}
value_array = dbus_glib_marshal_dbus_message_to_gvalue_array (message);
(* c_marshaller) (closure, return_value, arg, &expanded[0],
invocation_hint, marshal_data);
g_return_if_fail (value_array != NULL);
out:
i = 0;
while (i < arg)
{
g_value_unset (&expanded[i]);
++i;
}
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, value_array->n_values,
value_array->values, invocation_hint, marshal_data);
g_value_array_free (value_array);
}
static void

View file

@ -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
/**

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -40,7 +40,20 @@ test_dbus_glib_SOURCES= \
test_dbus_glib_LDADD= $(top_builddir)/glib/libdbus-glib-1.la
test_service_glib_SOURCES= \
test-service-glib.c
test-service-glib.c \
test-service-glib-glue.h
BUILT_SOURCES = test-service-glib-glue.h test-service-glib-bindings.h
test-service-glib-glue.h: $(top_builddir)/glib/dbus-binding-tool test-service-glib.xml
$(top_builddir)/glib/dbus-binding-tool --mode=glib-server $(srcdir)/test-service-glib.xml > test-service-glib-glue.h.tmp
mv test-service-glib-glue.h.tmp test-service-glib-glue.h
test-service-glib-bindings.h: $(top_builddir)/glib/dbus-binding-tool test-service-glib.xml
$(top_builddir)/glib/dbus-binding-tool --mode=glib-client $(srcdir)/test-service-glib.xml > test-service-glib-bindings.h.tmp
mv test-service-glib-bindings.h.tmp test-service-glib-bindings.h
CLEANFILES = test-service-glib-glue.h test-service-glib-glue.h.tmp test-service-glib-bindings.h test-service-glib-bindings.h.tmp
test_service_glib_LDADD= $(top_builddir)/glib/libdbus-glib-1.la

View file

@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test-service-glib-bindings.h"
static GMainLoop *loop = NULL;
static int n_times_foo_received = 0;
@ -37,7 +38,11 @@ main (int argc, char **argv)
int i;
guint32 result;
const char *v_STRING;
char *v_STRING_2;
guint32 v_UINT32;
guint32 v_UINT32_2;
double v_DOUBLE;
double v_DOUBLE_2;
g_type_init ();
@ -220,10 +225,204 @@ main (int argc, char **argv)
n_times_foo_received);
exit (1);
}
g_object_unref (G_OBJECT (driver));
/* Activate test servie */
g_print ("Activating TestSuiteGLibService\n");
v_STRING = "org.freedesktop.DBus.TestSuiteGLibService";
v_UINT32 = 0;
call = dbus_g_proxy_begin_call (driver, "StartServiceByName",
DBUS_TYPE_STRING,
&v_STRING,
DBUS_TYPE_UINT32,
&v_UINT32,
DBUS_TYPE_INVALID);
error = NULL;
if (!dbus_g_proxy_end_call (driver, call, &error,
DBUS_TYPE_UINT32, &result,
DBUS_TYPE_INVALID))
{
g_printerr ("Failed to complete Activate call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
g_object_unref (G_OBJECT (proxy));
proxy = dbus_g_proxy_new_for_name_owner (connection,
"org.freedesktop.DBus.TestSuiteGLibService",
"/org/freedesktop/DBus/Tests/MyTestObject",
"org.freedesktop.DBus.Tests.MyObject",
&error);
if (proxy == NULL)
{
g_printerr ("Failed to create proxy for name owner: %s\n",
error->message);
g_error_free (error);
exit (1);
}
call = dbus_g_proxy_begin_call (proxy, "DoNothing",
DBUS_TYPE_INVALID);
error = NULL;
if (!dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_INVALID))
{
g_printerr ("Failed to complete DoNothing call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
v_UINT32 = 42;
call = dbus_g_proxy_begin_call (proxy, "Increment",
DBUS_TYPE_UINT32, &v_UINT32,
DBUS_TYPE_INVALID);
error = NULL;
if (!dbus_g_proxy_end_call (proxy, call, &error,
DBUS_TYPE_UINT32, &v_UINT32_2,
DBUS_TYPE_INVALID))
{
g_printerr ("Failed to complete Increment call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (v_UINT32_2 != v_UINT32 + 1)
{
g_printerr ("Increment call returned %d, should be 43\n", v_UINT32_2);
exit (1);
}
call = dbus_g_proxy_begin_call (proxy, "ThrowError", DBUS_TYPE_INVALID);
error = NULL;
if (dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_INVALID) != FALSE)
{
g_printerr ("ThrowError call unexpectedly succeeded!\n");
exit (1);
}
g_print ("ThrowError failed (as expected) returned error: %s\n", error->message);
g_error_free (error);
v_STRING = "foobar";
call = dbus_g_proxy_begin_call (proxy, "Uppercase",
DBUS_TYPE_STRING, &v_STRING,
DBUS_TYPE_INVALID);
error = NULL;
if (!dbus_g_proxy_end_call (proxy, call, &error,
DBUS_TYPE_STRING, &v_STRING_2,
DBUS_TYPE_INVALID))
{
g_printerr ("Failed to complete Uppercase call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (strcmp ("FOOBAR", v_STRING_2) != 0)
{
g_printerr ("Uppercase call returned unexpected string %s\n", v_STRING_2);
exit (1);
}
v_STRING = "bazwhee";
v_UINT32 = 26;
v_DOUBLE = G_PI;
call = dbus_g_proxy_begin_call (proxy, "ManyArgs",
DBUS_TYPE_UINT32, &v_UINT32,
DBUS_TYPE_STRING, &v_STRING,
DBUS_TYPE_DOUBLE, &v_DOUBLE,
DBUS_TYPE_INVALID);
error = NULL;
if (!dbus_g_proxy_end_call (proxy, call, &error,
DBUS_TYPE_DOUBLE, &v_DOUBLE_2,
DBUS_TYPE_STRING, &v_STRING_2,
DBUS_TYPE_INVALID))
{
g_printerr ("Failed to complete ManyArgs call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (v_DOUBLE_2 < 55 || v_DOUBLE_2 > 56)
{
g_printerr ("ManyArgs call returned unexpected double value %f\n", v_DOUBLE_2);
exit (1);
}
if (strcmp ("BAZWHEE", v_STRING_2) != 0)
{
g_printerr ("ManyArgs call returned unexpected string %s\n", v_STRING_2);
exit (1);
}
if (!org_freedesktop_DBus_Tests_MyObject_do_nothing (proxy, &error))
{
g_printerr ("Failed to complete (wrapped) DoNothing call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (!org_freedesktop_DBus_Tests_MyObject_increment (proxy, 42, &v_UINT32_2, &error))
{
g_printerr ("Failed to complete (wrapped) Increment call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (v_UINT32_2 != 43)
{
g_printerr ("(wrapped) increment call returned %d, should be 43\n", v_UINT32_2);
exit (1);
}
if (org_freedesktop_DBus_Tests_MyObject_throw_error (proxy, &error) != FALSE)
{
g_printerr ("(wrapped) ThrowError call unexpectedly succeeded!\n");
exit (1);
}
g_print ("(wrapped) ThrowError failed (as expected) returned error: %s\n", error->message);
g_error_free (error);
if (!org_freedesktop_DBus_Tests_MyObject_uppercase (proxy, "foobar", &v_STRING_2, &error))
{
g_printerr ("Failed to complete (wrapped) Uppercase call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (strcmp ("FOOBAR", v_STRING_2) != 0)
{
g_printerr ("(wrapped) Uppercase call returned unexpected string %s\n", v_STRING_2);
exit (1);
}
if (!org_freedesktop_DBus_Tests_MyObject_many_args (proxy, 26, "bazwhee", G_PI,
&v_DOUBLE_2, &v_STRING_2, &error))
{
g_printerr ("Failed to complete (wrapped) ManyArgs call: %s\n",
error->message);
g_error_free (error);
exit (1);
}
if (v_DOUBLE_2 < 55 || v_DOUBLE_2 > 56)
{
g_printerr ("(wrapped) ManyArgs call returned unexpected double value %f\n", v_DOUBLE_2);
exit (1);
}
if (strcmp ("BAZWHEE", v_STRING_2) != 0)
{
g_printerr ("(wrapped) ManyArgs call returned unexpected string %s\n", v_STRING_2);
exit (1);
}
g_object_unref (G_OBJECT (proxy));
g_object_unref (G_OBJECT (driver));
g_print ("Successfully completed %s\n", argv[0]);
return 0;

View file

@ -4,6 +4,8 @@
#include <stdlib.h>
#include <string.h>
#include <glib/gi18n.h>
#include <glib-object.h>
#include <glib/gquark.h>
typedef struct MyObject MyObject;
typedef struct MyObjectClass MyObjectClass;
@ -30,6 +32,28 @@ struct MyObjectClass
G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT)
typedef enum
{
MY_OBJECT_ERROR_FOO,
MY_OBJECT_ERROR_BAR
} MyObjectError;
#define MY_OBJECT_ERROR my_object_error_quark ()
gboolean my_object_do_nothing (MyObject *obj, GError **error);
gboolean my_object_increment (MyObject *obj, gint32 x, int *ret, GError **error);
gboolean my_object_throw_error (MyObject *obj, GError **error);
gboolean my_object_uppercase (MyObject *obj, const char *str, char **ret, GError **error);
gboolean my_object_many_args (MyObject *obj, guint32 x, const char *str, double trouble, double *d_ret, char **str_ret, GError **error);
#include "test-service-glib-glue.h"
GQuark my_object_error_quark (void);
/* Properties */
enum
{
@ -115,6 +139,54 @@ my_object_class_init (MyObjectClass *mobject_class)
"default value",
G_PARAM_READWRITE));
}
GQuark
my_object_error_quark (void)
{
static GQuark quark = 0;
if (!quark)
quark = g_quark_from_static_string ("my_object_error");
return quark;
}
gboolean
my_object_do_nothing (MyObject *obj, GError **error)
{
return TRUE;
}
gboolean
my_object_increment (MyObject *obj, gint32 x, int *ret, GError **error)
{
*ret = x +1;
return TRUE;
}
gboolean
my_object_throw_error (MyObject *obj, GError **error)
{
g_set_error (error,
MY_OBJECT_ERROR,
MY_OBJECT_ERROR_FOO,
"this method always loses");
return FALSE;
}
gboolean
my_object_uppercase (MyObject *obj, const char *str, char **ret, GError **error)
{
*ret = g_ascii_strup (str, -1);
return TRUE;
}
gboolean
my_object_many_args (MyObject *obj, guint32 x, const char *str, double trouble, double *d_ret, char **str_ret, GError **error)
{
*d_ret = trouble + (x * 2);
*str_ret = g_ascii_strup (str, -1);
return TRUE;
}
static GMainLoop *loop;
@ -148,8 +220,10 @@ main (int argc, char **argv)
obj = g_object_new (MY_TYPE_OBJECT, NULL);
dbus_g_object_class_install_info (G_OBJECT_GET_CLASS (obj),
&dbus_glib_my_object_object_info);
dbus_g_connection_register_g_object (connection,
"/org/freedesktop/my_test_object",
"/org/freedesktop/DBus/Tests/MyTestObject",
obj);
driver_proxy = dbus_g_proxy_new_for_name (connection,

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/org/freedesktop/DBus/Tests/MyTestObject">
<interface name="org.freedesktop.DBus.Tests.MyObject" c_name="my_object">
<method name="DoNothing" c_name="my_object_do_nothing">
</method>
<method name="Increment" c_name="my_object_increment">
<arg type="uint32" name="x" />
<arg type="uint32" direction="out" />
</method>
<method name="ThrowError" c_name="my_object_throw_error">
</method>
<method name="Uppercase" c_name="my_object_uppercase">
<arg type="string" direction="in" />
<arg type="string" direction="out" />
</method>
<method name="ManyArgs" c_name="my_object_many_args">
<arg type="uint32" name="x" direction="in" />
<arg type="string" name="str" direction="in" />
<arg type="double" name="trouble" direction="in" />
<arg type="double" name="d_ret" direction="out" />
<arg type="string" name="str_ret" direction="out" />
</method>
</interface>
</node>