From 917e0632cb4e8a4508112075b48a608ac36f8c33 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Tue, 15 Jun 2010 14:37:47 -0400 Subject: Initial commit --- Makefile.am | 2 + autogen.sh | 90 +++++ configure.ac | 117 ++++++ src/Makefile.am | 67 ++++ src/gdbusgi.c | 1012 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/gdbusgi.h | 41 +++ src/myfrobnicator.c | 274 ++++++++++++++ src/myfrobnicator.h | 112 ++++++ src/test.c | 347 ++++++++++++++++++ 9 files changed, 2062 insertions(+) create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 src/Makefile.am create mode 100644 src/gdbusgi.c create mode 100644 src/gdbusgi.h create mode 100644 src/myfrobnicator.c create mode 100644 src/myfrobnicator.h create mode 100644 src/test.c diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..2cad62a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = src diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..29cfc2b --- /dev/null +++ b/autogen.sh @@ -0,0 +1,90 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +set -e +set -x + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +DIE=0 + +(test -f $srcdir/configure.ac) || { + echo -n "**Error**: Directory $srcdir does not look like the" + echo " top-level package directory" + exit 1 +} + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have autoconf installed." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have libtool installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have automake installed." + echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing aclocal. The version of automake" + echo "installed doesn't appear recent enough." + echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run configure with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo $0 " command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +aclocalinclude="$ACLOCAL_FLAGS" + +echo "Running libtoolize..." +libtoolize --force --copy +echo "Running aclocal $aclocalinclude ..." +aclocal $aclocalinclude +echo "Running autoheader..." +autoheader +echo "Running automake --foreign -Wno-portability $am_opt ..." +automake --add-missing --foreign -Wno-portability $am_opt +echo "Running autoconf ..." +autoconf + +conf_flags="--enable-maintainer-mode --enable-gtk-doc" + +if test x$NOCONFIGURE = x; then + echo "Running $srcdir/configure $conf_flags $@ ..." + $srcdir/configure $conf_flags "$@" \ + && echo "Now type make to compile." || exit 1 +else + echo "Skipping configure process." +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..cddd86a --- /dev/null +++ b/configure.ac @@ -0,0 +1,117 @@ + +AC_INIT([GDBus for GObject Introspection],[0.1],[mailto:zeuthen@gmail.com],[gdbus-gi]) +AM_INIT_AUTOMAKE([1.9 foreign dist-bzip2 no-dist-gzip]) +AC_CONFIG_HEADERS([config.h]) +AM_MAINTAINER_MODE + +AC_PROG_CC +AC_ISC_POSIX +AC_HEADER_STDC +AM_PROG_LIBTOOL + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +#### gcc warning flags + +if test "x$GCC" = "xyes"; then + changequote(,)dnl + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wchar-subscripts[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wchar-subscripts" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wmissing-declarations[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wmissing-declarations" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wnested-externs[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wnested-externs" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wpointer-arith[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wpointer-arith" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wcast-align[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wcast-align" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wsign-compare[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wsign-compare" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wformat[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wformat" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-Wformat-security[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wformat-security" ;; + esac + + if test "x$enable_ansi" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-ansi[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -ansi" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-D_POSIX_C_SOURCE*) ;; + *) CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199309L" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-D_BSD_SOURCE[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -D_BSD_SOURCE" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-pedantic[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -pedantic" ;; + esac + fi + changequote([,])dnl +fi + +PKG_CHECK_MODULES(GLIB2, glib-2.0 gio-2.0) +AC_SUBST(GLIB2_CFLAGS) +AC_SUBST(GLIB2_LIBS) + +PKG_CHECK_MODULES([INTROSPECTION], [gobject-introspection-1.0 >= 0.6.2]) + +AC_OUTPUT([ +Makefile +src/Makefile +]) + +echo " + gdbus-gi $VERSION + =============== + + prefix: ${prefix} + libdir: ${libdir} + libexecdir: ${libexecdir} + bindir: ${bindir} + sbindir: ${sbindir} + datadir: ${datadir} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} + docdir: ${docdir} + + compiler: ${CC} + cflags: ${CFLAGS} + cppflags: ${CPPFLAGS} + + Maintainer mode: ${USE_MAINTAINER_MODE} +" diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..0cc9721 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,67 @@ + +NULL = + +noinst_PROGRAMS = test + +test_SOURCES = \ + test.c \ + gdbusgi.h gdbusgi.c \ + $(NULL) + +test_CFLAGS = \ + $(GLIB2_CFLAGS) \ + $(INTROSPECTION_CFLAGS) \ + $(NULL) + +test_LDADD = \ + $(GLIB2_LIBS) \ + $(INTROSPECTION_LIBS) \ + libmy.la \ + $(NULL) + +# ---------------------------------------------------------------------------------------------------- + +noinst_LTLIBRARIES = libmy.la + +libmy_la_SOURCES = \ + myfrobnicator.h myfrobnicator.c \ + gdbusgi.h gdbusgi.c \ + $(NULL) + +libmy_la_CFLAGS = \ + $(GLIB2_CFLAGS) \ + $(INTROSPECTION_CFLAGS) \ + $(NULL) + +libmy_la_LIBADD = \ + $(GLIB2_LIBS) \ + $(INTROSPECTION_LIBS) \ + $(NULL) + +# ---------------------------------------------------------------------------------------------------- + +My-1.0.gir: libmy.la Makefile.am + g-ir-scanner -v -v \ + --namespace My \ + --nsversion=1.0 \ + --include=Gio-2.0 \ + --library=libmy.la \ + --libtool ../libtool \ + --output $@ \ + --pkg=glib-2.0 \ + --pkg=gobject-2.0 \ + --pkg=gio-2.0 \ + -I$(top_builddir)/src \ + -I$(top_srcdir)/src \ + $(top_srcdir)/src/myfrobnicator.h \ + $(top_srcdir)/src/myfrobnicator.c \ + $(NULL) + +My-1.0.typelib: My-1.0.gir + g-ir-compiler $< -o $@ + +# ---------------------------------------------------------------------------------------------------- + +clean-local: + rm -f *~ My* + diff --git a/src/gdbusgi.c b/src/gdbusgi.c new file mode 100644 index 0000000..1578d6d --- /dev/null +++ b/src/gdbusgi.c @@ -0,0 +1,1012 @@ +/* + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "gdbusgi.h" + +#include + +typedef struct +{ + GObject *object; + GDBusInterfaceInfo *info; + gpointer user_data; + GDestroyNotify user_data_free_func; + + GHashTable *dbus_method_name_to_method_data; +} ExportData; + +static void +export_data_free (ExportData *data) +{ + g_hash_table_unref (data->dbus_method_name_to_method_data); + if (data->object != NULL) + g_object_unref (data->object); + if (data->info != NULL) + g_dbus_interface_info_unref (data->info); + if (data->user_data_free_func != NULL) + data->user_data_free_func (data->user_data); + g_free (data); +} + +typedef struct +{ + GIFunctionInfo *method; + GDBusMethodInfo *dbus_method; + guint num_gi_in_args; + guint num_gi_out_args; +} MethodData; + +static void +method_data_free (MethodData *data) +{ + g_base_info_unref (data->method); + g_dbus_method_info_unref (data->dbus_method); + g_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +set_in_arg (GVariant *value, + GArgument *arg_to_set, + GIArgInfo *arg_info) +{ + const GVariantType *variant_type; + + variant_type = g_variant_get_type (value); + + if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN)) + arg_to_set->v_boolean = g_variant_get_boolean (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BYTE)) + arg_to_set->v_int8 = g_variant_get_byte (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT16)) + arg_to_set->v_int16 = g_variant_get_int16 (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT16)) + arg_to_set->v_uint16 = g_variant_get_uint16 (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT32)) + arg_to_set->v_int32 = g_variant_get_int32 (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT32)) + arg_to_set->v_uint32 = g_variant_get_uint32 (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT64)) + arg_to_set->v_int64 = g_variant_get_int64 (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT64)) + arg_to_set->v_uint64 = g_variant_get_uint64 (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_DOUBLE)) + arg_to_set->v_double = g_variant_get_double (value); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_STRING) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_OBJECT_PATH) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_SIGNATURE)) + { + switch (g_arg_info_get_ownership_transfer (arg_info)) + { + case GI_TRANSFER_NOTHING: + arg_to_set->v_string = (gchar *) g_variant_get_string (value, NULL); + break; + case GI_TRANSFER_CONTAINER: + g_assert_not_reached (); + break; + case GI_TRANSFER_EVERYTHING: + arg_to_set->v_string = g_variant_dup_string (value, NULL); + break; + } + } + else + { + g_error ("Don't know how to set GVariant with type-string %s on a GArgument", + g_variant_get_type_string (value)); + } +} + + +static void +prepare_in_args (GVariant *parameters, + GArgument *in_args, + guint num_in_args, + GIFunctionInfo *method) +{ + GVariantIter iter; + GICallableInfo *callable; + guint count_in_args; + guint count_out_args; + guint n; + + g_assert_cmpint (g_variant_n_children (parameters), ==, num_in_args); + + callable = (GICallableInfo *) method; + + count_in_args = 0; + count_out_args = 0; + g_variant_iter_init (&iter, parameters); + for (n = 0; n < (guint) g_callable_info_get_n_args (callable); n++) + { + GArgument *arg_to_set; + GIArgInfo *arg; + GIDirection direction; + + arg = g_callable_info_get_arg (callable, n); + direction = g_arg_info_get_direction (arg); + arg_to_set = NULL; + switch (direction) + { + case GI_DIRECTION_IN: + arg_to_set = &in_args[count_in_args++]; + break; + + case GI_DIRECTION_OUT: + /* do nothing */ + count_out_args++; + break; + + case GI_DIRECTION_INOUT: + count_out_args++; + arg_to_set = &in_args[count_in_args++]; + break; + } + + if (arg_to_set != NULL) + { + GVariant *item; + + item = g_variant_iter_next_value (&iter); + g_assert (item != NULL); + set_in_arg (item, arg_to_set, arg); + } + } + g_assert_cmpint (count_in_args, ==, num_in_args); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +add_out_arg (GVariantBuilder *builder, + GITypeInfo *type, + GArgument *value, + GDBusArgInfo *arg_info) +{ + const GVariantType *variant_type; + GVariant *variant; + + variant_type = G_VARIANT_TYPE (arg_info->signature); + + if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN)) + variant = g_variant_new_boolean (value->v_boolean); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_BYTE)) + variant = g_variant_new_byte (value->v_uint8); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT16)) + variant = g_variant_new_int16 (value->v_int16); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT16)) + variant = g_variant_new_uint16 (value->v_uint16); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT32)) + variant = g_variant_new_int32 (value->v_int32); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT32)) + variant = g_variant_new_uint32 (value->v_uint32); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT64)) + variant = g_variant_new_int64 (value->v_int64); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT64)) + variant = g_variant_new_uint64 (value->v_uint64); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_DOUBLE)) + variant = g_variant_new_double (value->v_double); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_STRING)) + variant = g_variant_new_string (value->v_string); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_OBJECT_PATH)) + variant = g_variant_new_object_path (value->v_string); + else if (g_variant_type_equal (variant_type, G_VARIANT_TYPE_SIGNATURE)) + variant = g_variant_new_signature (value->v_string); + else + g_assert_not_reached (); + + g_variant_builder_add_value (builder, variant); +} + +static void +prepare_out_args (GArgument *return_value, + GArgument *args, + GArgument *arg_values, + guint num_args, + GIFunctionInfo *method, + GDBusMethodInfo *dbus_method) +{ + GICallableInfo *callable; + guint n; + guint count_out_args; + + callable = (GICallableInfo *) method; + + count_out_args = 0; + for (n = 0; n < (guint) g_callable_info_get_n_args (callable); n++) + { + GIArgInfo *arg; + GIDirection direction; + + arg = g_callable_info_get_arg (callable, n); + direction = g_arg_info_get_direction (arg); + + switch (direction) + { + case GI_DIRECTION_IN: + /* do nothing */ + break; + + case GI_DIRECTION_OUT: + args[count_out_args].v_pointer = &arg_values[count_out_args]; + count_out_args++; + break; + + case GI_DIRECTION_INOUT: + /* won't be used but needs to be present */ + args[count_out_args].v_pointer = NULL; + count_out_args++; + break; + } + g_base_info_unref ((GIBaseInfo *) arg); + } + g_assert_cmpint (num_args, ==, count_out_args); +} + +static GVariant * +process_out_args (GArgument *return_value, + GArgument *in_args, + GArgument *args, + guint num_args, + GIFunctionInfo *method, + GDBusMethodInfo *dbus_method) +{ + GVariant *ret; + GVariantBuilder builder; + GICallableInfo *callable; + GITypeInfo *return_type; + guint dbus_arg_num; + guint n; + guint count_in_args; + guint count_out_args; + + callable = (GICallableInfo *) method; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); + + dbus_arg_num = 0; + return_type = g_callable_info_get_return_type (callable); + if (g_type_info_get_tag (return_type) != GI_TYPE_TAG_VOID) + { + add_out_arg (&builder, + return_type, + return_value, + dbus_method->out_args[dbus_arg_num++]); + } + //g_base_info_unref ((GIBaseInfo *) return_type); + + count_in_args = 0; + count_out_args = 0; + for (n = 0; n < (guint) g_callable_info_get_n_args (callable); n++) + { + GIArgInfo *arg; + GIDirection direction; + GArgument *arg_with_value; + + arg = g_callable_info_get_arg (callable, n); + direction = g_arg_info_get_direction (arg); + + arg_with_value = NULL; + switch (direction) + { + case GI_DIRECTION_IN: + /* do nothing */ + count_in_args++; + break; + + case GI_DIRECTION_OUT: + arg_with_value = args[count_out_args].v_pointer; + count_out_args++; + break; + + case GI_DIRECTION_INOUT: + /* return value is in the in GArgument, not the out one (that needs to be present) */ + arg_with_value = &in_args[count_in_args]; + count_out_args++; + count_in_args++; + break; + } + + if (arg_with_value != NULL) + { + add_out_arg (&builder, + return_type, + arg_with_value, + dbus_method->out_args[dbus_arg_num++]); + } + + g_base_info_unref ((GIBaseInfo *) arg); + } + g_assert_cmpint (num_args, ==, count_out_args); + + ret = g_variant_builder_end (&builder); + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +free_one_arg (GArgument *arg, + GITypeInfo *type, + GITransfer transfer) +{ + GITypeTag tag; + + tag = g_type_info_get_tag (type); + switch (tag) + { + case GI_TYPE_TAG_UTF8: + switch (transfer) + { + case GI_TRANSFER_NOTHING: + /* do nothing */ + break; + case GI_TRANSFER_CONTAINER: + g_assert_not_reached (); + break; + case GI_TRANSFER_EVERYTHING: + g_free (arg->v_string); + break; + } + break; + + default: + /* Do nothing */ + break; + } +} + +static void +free_args (GArgument *return_value, + GArgument *in_args, + guint num_in_args, + GArgument *out_args, + GArgument *out_arg_values, + guint num_out_args, + GIFunctionInfo *method) +{ + GICallableInfo *callable; + guint count_in_args; + guint count_out_args; + guint n; + GITypeInfo *return_type; + + callable = (GICallableInfo *) method; + + return_type = g_callable_info_get_return_type (callable); + if (g_type_info_get_tag (return_type) != GI_TYPE_TAG_VOID) + { + free_one_arg (return_value, + return_type, + g_callable_info_get_caller_owns (callable)); + } + //g_base_info_unref ((GIBaseInfo *) return_type); + + count_in_args = 0; + count_out_args = 0; + for (n = 0; n < (guint) g_callable_info_get_n_args (callable); n++) + { + GIArgInfo *arg; + GITypeInfo *type; + GIDirection direction; + GITransfer transfer; + GArgument *arg_to_free; + + arg = g_callable_info_get_arg (callable, n); + type = g_arg_info_get_type (arg); + direction = g_arg_info_get_direction (arg); + transfer = g_arg_info_get_ownership_transfer (arg); + + switch (direction) + { + case GI_DIRECTION_IN: + /* do nothing */ + arg_to_free = &in_args[count_in_args++]; + break; + + case GI_DIRECTION_OUT: + arg_to_free = &out_arg_values[count_out_args++]; + break; + + case GI_DIRECTION_INOUT: + /* should be unused */ + g_assert (out_args[count_out_args].v_pointer == NULL); + count_out_args++; + arg_to_free = &in_args[count_in_args++]; + break; + } + + free_one_arg (arg_to_free, + type, + transfer); + + g_base_info_unref ((GIBaseInfo *) type); + g_base_info_unref ((GIBaseInfo *) arg); + } + g_assert_cmpint (num_in_args, ==, count_in_args); + g_assert_cmpint (num_out_args, ==, count_out_args); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GStaticPrivate gobject_invocation = G_STATIC_PRIVATE_INIT; +static GStaticPrivate gobject_invocation_object = G_STATIC_PRIVATE_INIT; +static GStaticPrivate gobject_invocation_deferred = G_STATIC_PRIVATE_INIT; + +static void +on_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + ExportData *data = user_data; + MethodData *method_data; + GError *error; + GArgument return_value; + GArgument *in_args; + GArgument *out_args; + GArgument *out_arg_values; + guint n; + gboolean deferred; + gboolean invocation_result; + + in_args = NULL; + out_args = NULL; + + /* The GDBus core guarantees that only methods matching the given + * introspection data is dispatched. + */ + method_data = g_hash_table_lookup (data->dbus_method_name_to_method_data, method_name); + g_assert (method_data != NULL); + + /* First argument is the object (e.g. this/self) and not included in the arg count */ + in_args = g_new0 (GArgument, method_data->num_gi_in_args + 1); + out_args = g_new0 (GArgument, method_data->num_gi_out_args); + out_arg_values = g_new0 (GArgument, method_data->num_gi_out_args); + n = 0; + in_args[n++].v_pointer = data->object; + + prepare_in_args (parameters, + in_args + 1, + method_data->num_gi_in_args, + method_data->method); + prepare_out_args (&return_value, + out_args, + out_arg_values, + method_data->num_gi_out_args, + method_data->method, + method_data->dbus_method); + + g_static_private_set (&gobject_invocation, invocation, NULL); + g_static_private_set (&gobject_invocation_object, g_object_ref (data->object), g_object_unref); + g_static_private_set (&gobject_invocation_deferred, GINT_TO_POINTER (FALSE), NULL); + + error = NULL; + invocation_result = g_function_info_invoke (method_data->method, + in_args, + method_data->num_gi_in_args + 1, + out_args, + method_data->num_gi_out_args, + &return_value, + &error); + g_static_private_set (&gobject_invocation, NULL, NULL); + g_static_private_set (&gobject_invocation_object, NULL, NULL); + if (!invocation_result) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + goto out; + } + + deferred = GPOINTER_TO_INT (g_static_private_get (&gobject_invocation_deferred)); + if (!deferred) + { + GVariant *return_gvariant; + return_gvariant = process_out_args (&return_value, + in_args + 1, /* skip this/self */ + out_args, + method_data->num_gi_out_args, + method_data->method, + method_data->dbus_method); + g_dbus_method_invocation_return_value (invocation, return_gvariant); + g_variant_unref (return_gvariant); + } + + out: + if (in_args != NULL) + { + free_args (&return_value, + in_args + 1, /* skip this/self */ + method_data->num_gi_in_args, + out_args, + out_arg_values, + method_data->num_gi_out_args, + method_data->method); + g_free (in_args); + g_free (out_args); + g_free (out_arg_values); + } +} + +/** + * g_dbus_method_invocation_get_for_gobject: + * @object: A #GObject registered with g_dbus_connection_register_gobject(). + * @takeover_invocation: Whether the invocation should be taken over. + * + * Gets the #GDBusMethodInvocation representing the remote call being + * dispatched on the thread, if any. This can be used to get + * information about the remote end via + * e.g. g_dbus_method_invocation_get_sender(). + * + * It is safe to call this multiple times. + * + * Returns: A #GDBusMethodInvocation object for the method call + * currently being dispatced or %NULL if the invocation of the + * method isn't for handling a remote D-Bus call. Do not free + * the reference. + */ +GDBusMethodInvocation * +g_dbus_method_invocation_get_for_gobject (GObject *object) +{ + GDBusMethodInvocation *ret; + GObject *object_for_invocation; + + ret = G_DBUS_METHOD_INVOCATION (g_static_private_get (&gobject_invocation)); + if (ret == NULL) + goto out; + + object_for_invocation = G_OBJECT (g_static_private_get (&gobject_invocation_object)); + g_warn_if_fail (object == object_for_invocation); + + out: + return ret; +} + +/** + * g_dbus_method_invocation_takeover_for_gobject: + * @object: A #GObject registered with g_dbus_connection_register_gobject(). + * + * Gets the #GDBusMethodInvocation representing the remote D-Bus call + * currently being dispatched on the running thread, if any. By doing + * this, the caller assumes the responsibility of finishing the remote + * D-Bus call. This means that all handling of return values and + * errors from the calling method is ignored and handling of the D-Bus + * call must be finished using + * e.g. g_dbus_method_invocation_return_value() or + * g_dbus_method_invocation_return_error() or g_object_unref(). + * + * It is a programming error to call this more than once. + * + * Returns: A #GDBusMethodInvocation object for the method call + * currently being dispatced or %NULL if the invocation of the method + * isn't for handling a remote D-Bus call. Caller owns the returned + * reference. + */ +GDBusMethodInvocation * +g_dbus_method_invocation_takeover_for_gobject (GObject *object) +{ + GDBusMethodInvocation *ret; + gboolean already_deferred; + + ret = g_dbus_method_invocation_get_for_gobject (object); + if (ret == NULL) + goto out; + + already_deferred = GPOINTER_TO_INT (g_static_private_get (&gobject_invocation_deferred)); + g_warn_if_fail (!already_deferred); + g_static_private_set (&gobject_invocation_deferred, GINT_TO_POINTER (TRUE), NULL); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GVariant * +on_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + g_assert_not_reached (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +on_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + g_assert_not_reached (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static const GDBusInterfaceVTable export_vtable = +{ + on_method_call, + on_get_property, + on_set_property +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static GDBusArgInfo * +get_arg_info (GICallableInfo *callable, + GIArgInfo *arg, + GError **error) +{ + GITypeTag tag; + GITypeInfo *type; + GDBusArgInfo *ret; + GVariantType *variant_type; + const gchar *type_from_annotation; + const gchar *name; + + ret = NULL; + + type_from_annotation = NULL; + if (arg != NULL) + { + type = g_arg_info_get_type (arg); + name = g_base_info_get_name ((GIBaseInfo *) arg); + type_from_annotation = g_base_info_get_attribute ((GIBaseInfo *) arg, "gdbus.signature"); + } + else + { + type = g_callable_info_get_return_type (callable); + name = "return_value"; + type_from_annotation = g_callable_info_get_return_attribute (callable, "gdbus.signature"); + } + + if (type_from_annotation != NULL) + { + variant_type = g_variant_type_new (type_from_annotation); + /* TODO: check that @type is actually compatible with @type_from_annotation */ + } + else + { + tag = g_type_info_get_tag (type); + switch (tag) + { + case GI_TYPE_TAG_BOOLEAN: + variant_type = g_variant_type_new ("b"); + break; + case GI_TYPE_TAG_INT8: + variant_type = g_variant_type_new ("y"); + break; + case GI_TYPE_TAG_UINT8: + variant_type = g_variant_type_new ("y"); + break; + case GI_TYPE_TAG_INT16: + variant_type = g_variant_type_new ("n"); + break; + case GI_TYPE_TAG_UINT16: + variant_type = g_variant_type_new ("q"); + break; + case GI_TYPE_TAG_INT: + case GI_TYPE_TAG_INT32: + variant_type = g_variant_type_new ("i"); + break; + case GI_TYPE_TAG_UINT: + case GI_TYPE_TAG_UINT32: + variant_type = g_variant_type_new ("u"); + break; + case GI_TYPE_TAG_INT64: + variant_type = g_variant_type_new ("x"); + break; + case GI_TYPE_TAG_UINT64: + variant_type = g_variant_type_new ("t"); + break; + case GI_TYPE_TAG_DOUBLE: + variant_type = g_variant_type_new ("d"); + break; + case GI_TYPE_TAG_UTF8: + variant_type = g_variant_type_new ("s"); + break; + case GI_TYPE_TAG_FILENAME: + variant_type = g_variant_type_new ("ay"); + break; + default: + variant_type = NULL; + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Type tag `%s' not supported", + g_type_tag_to_string (tag)); + goto out; + } + } + + ret = g_new0 (GDBusArgInfo, 1); + ret->ref_count = 1; + ret->name = g_strdup (name); + ret->signature = g_variant_type_dup_string (variant_type); + + out: + g_base_info_unref ((GIBaseInfo *) type); + return ret; +} + +static GDBusMethodInfo * +get_method_info (GIFunctionInfo *method, + const gchar *dbus_name, + gint *num_gi_in_args, + gint *num_gi_out_args, + GError **error) +{ + GDBusMethodInfo *ret; + GICallableInfo *callable; + GITypeInfo *return_type; + GPtrArray *in_args; + GPtrArray *out_args; + guint n; + + ret = NULL; + in_args = g_ptr_array_new (); + out_args = g_ptr_array_new (); + + *num_gi_in_args = 0; + *num_gi_out_args = 0; + + callable = (GICallableInfo *) method; + + /* Handle return value, if any */ + return_type = g_callable_info_get_return_type (callable); + if (g_type_info_get_tag (return_type) != GI_TYPE_TAG_VOID) + { + GDBusArgInfo *arg_info; + arg_info = get_arg_info (callable, NULL, error); + if (arg_info == NULL) + { + g_prefix_error (error, + "Error processing return value of symbol `%s': ", + g_function_info_get_symbol (method)); + g_base_info_unref ((GIBaseInfo *) return_type); + goto out; + } + g_ptr_array_add (out_args, arg_info); + } + //g_base_info_unref ((GIBaseInfo *) return_type); + + /* Go through all parameters */ + for (n = 0; n < (guint) g_callable_info_get_n_args (callable); n++) + { + GIArgInfo *arg; + GIDirection direction; + GDBusArgInfo *arg_info; + + arg = g_callable_info_get_arg (callable, n); + + arg_info = get_arg_info (callable, arg, error); + if (arg_info == NULL) + { + g_prefix_error (error, + "Error processing argument `%s' of symbol `%s': ", + g_base_info_get_name ((GIBaseInfo *) arg), + g_function_info_get_symbol (method)); + g_base_info_unref ((GIBaseInfo *) arg); + goto out; + } + + direction = g_arg_info_get_direction (arg); + switch (direction) + { + case GI_DIRECTION_IN: + g_ptr_array_add (in_args, arg_info); + *num_gi_in_args += 1; + break; + case GI_DIRECTION_OUT: + g_ptr_array_add (out_args, arg_info); + *num_gi_out_args += 1; + break; + case GI_DIRECTION_INOUT: + { + gchar *s; + s = g_strdup_printf ("in_%s", arg_info->name); + g_free (arg_info->name); + arg_info->name = s; + g_ptr_array_add (in_args, arg_info); + *num_gi_in_args += 1; + + arg_info = get_arg_info (callable, arg, NULL); + g_assert (arg_info != NULL); + s = g_strdup_printf ("out_%s", arg_info->name); + g_free (arg_info->name); + arg_info->name = s; + g_ptr_array_add (out_args, arg_info); + *num_gi_out_args += 1; + break; + } + } + g_base_info_unref ((GIBaseInfo *) arg); + } + + g_ptr_array_add (in_args, NULL); + g_ptr_array_add (out_args, NULL); + + ret = g_new0 (GDBusMethodInfo, 1); + ret->ref_count = 1; + ret->name = g_strdup (dbus_name); + g_ptr_array_ref (in_args); + g_ptr_array_ref (out_args); + ret->in_args = (GDBusArgInfo **) g_ptr_array_free (in_args, FALSE); + ret->out_args = (GDBusArgInfo **) g_ptr_array_free (out_args, FALSE); + + out: + g_ptr_array_foreach (in_args, (GFunc) g_dbus_arg_info_unref, NULL); + g_ptr_array_foreach (out_args, (GFunc) g_dbus_arg_info_unref, NULL); + g_ptr_array_unref (in_args); + g_ptr_array_unref (out_args); + return ret; +} + +static GDBusInterfaceInfo * +compute_introspection_data (GObject *object, + ExportData *data, + GError **error) +{ + GPtrArray *method_infos; + GDBusInterfaceInfo *ret; + GIRepository *repo; + GIBaseInfo *info; + guint n; + + ret = NULL; + info = NULL; + + repo = g_irepository_get_default (); + info = g_irepository_find_by_gtype (repo, G_OBJECT_TYPE (object)); + if (info == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Error looking up GIBaseInfo for object of type %s", + g_type_name (G_OBJECT_TYPE (object))); + goto out; + } + + ret = g_new0 (GDBusInterfaceInfo, 1); + ret->ref_count = 1; + ret->name = g_strdup (g_base_info_get_attribute (info, "gdbus.interface")); + if (ret->name == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "No gdbus.interface attribute on type %s", + g_type_name (G_OBJECT_TYPE (object))); + g_dbus_interface_info_unref (ret); + ret = NULL; + goto out; + } + + g_assert (GI_IS_OBJECT_INFO (info)); + + method_infos = g_ptr_array_new (); + for (n = 0; n < (guint) g_object_info_get_n_methods ((GIObjectInfo *) info); n++) + { + GIFunctionInfo *method; + const gchar *dbus_name; + + method = g_object_info_get_method ((GIObjectInfo *) info, n); + dbus_name = g_base_info_get_attribute ((GIBaseInfo *) method, "gdbus.method"); + if (dbus_name != NULL) + { + GDBusMethodInfo *method_info; + MethodData *method_data; + gint num_gi_in_args; + gint num_gi_out_args; + + method_info = get_method_info (method, + dbus_name, + &num_gi_in_args, + &num_gi_out_args, + error); + if (method_info == NULL) + { + g_ptr_array_foreach (method_infos, (GFunc) g_dbus_method_info_unref, NULL); + g_ptr_array_unref (method_infos); + g_dbus_interface_info_unref (ret); + ret = NULL; + goto out; + } + g_ptr_array_add (method_infos, method_info); + + method_data = g_new0 (MethodData, 1); + method_data->method = g_base_info_ref ((GIBaseInfo *) method); + method_data->dbus_method = g_dbus_method_info_ref (method_info); + method_data->num_gi_in_args = num_gi_in_args; + method_data->num_gi_out_args = num_gi_out_args; + + g_hash_table_insert (data->dbus_method_name_to_method_data, + g_strdup (dbus_name), + method_data); + } + } + g_ptr_array_add (method_infos, NULL); + ret->methods = (GDBusMethodInfo **) g_ptr_array_free (method_infos, FALSE); + + out: + if (info != NULL) + g_base_info_unref (info); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +guint +g_dbus_connection_register_gobject (GDBusConnection *connection, + GObject *object, + const gchar *object_path, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error) +{ + ExportData *data; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + g_return_val_if_fail (G_IS_OBJECT (object), 0); + g_return_val_if_fail (g_variant_is_object_path (object_path), 0); + g_return_val_if_fail (error == NULL || *error == NULL, 0); + + data = g_new0 (ExportData, 1); + data->object = g_object_ref (object); + data->user_data = user_data; + data->user_data_free_func = user_data_free_func; + + data->dbus_method_name_to_method_data = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) method_data_free); + + data->info = compute_introspection_data (object, + data, + error); + if (data->info == NULL) + goto fail; + + return g_dbus_connection_register_object (connection, + object_path, + data->info, + &export_vtable, + data, + (GDestroyNotify) export_data_free, + error); + fail: + export_data_free (data); + return 0; +} diff --git a/src/gdbusgi.h b/src/gdbusgi.h new file mode 100644 index 0000000..86444ad --- /dev/null +++ b/src/gdbusgi.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __G_DBUS_GI_H__ +#define __G_DBUS_GI_H__ + +#include + +G_BEGIN_DECLS + +guint g_dbus_connection_register_gobject (GDBusConnection *connection, + GObject *object, + const gchar *object_path, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error); + +GDBusMethodInvocation *g_dbus_method_invocation_get_for_gobject (GObject *object); +GDBusMethodInvocation *g_dbus_method_invocation_takeover_for_gobject (GObject *object); + +G_END_DECLS + +#endif /* __G_DBUS_GI_H__ */ diff --git a/src/myfrobnicator.c b/src/myfrobnicator.c new file mode 100644 index 0000000..fc78fde --- /dev/null +++ b/src/myfrobnicator.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include "gdbusgi.h" + +#include "myfrobnicator.h" + +struct _MyFrobnicatorPrivate +{ + guint foo; +}; + +G_DEFINE_TYPE (MyFrobnicator, my_frobnicator, G_TYPE_OBJECT); + +static void +my_frobnicator_init (MyFrobnicator *frobnicator) +{ +} + +static void +my_frobnicator_class_init (MyFrobnicatorClass *klass) +{ +} + +MyFrobnicator * +my_frobnicator_new (void) +{ + return MY_FROBNICATOR (g_object_new (MY_TYPE_FROBNICATOR, NULL)); +} + +/** + * my_frobnicator_hello_world: + * @frobnicator: A #MyFrobnicator. + * @greeting: A greeting! + * + * Computes a response to @greeting. + * + * Returns: The response. Free with g_free(). + * + * Attributes: (gdbus.method HelloWorld) (foo.bar baz) + */ +gchar * +my_frobnicator_hello_world (MyFrobnicator *frobnicator, + const gchar *greeting) +{ + gchar *response; + + g_return_val_if_fail (MY_IS_FROBNICATOR (frobnicator), NULL); + g_return_val_if_fail (greeting != NULL, NULL); + + response = NULL; + + if (g_strcmp0 (greeting, "fail") == 0) + { + GDBusMethodInvocation *invocation; + invocation = g_dbus_method_invocation_takeover_for_gobject (G_OBJECT (frobnicator)); + g_dbus_method_invocation_return_error (invocation, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "fail boat!"); + } + else + { + response = g_strdup_printf ("Hey %s - you greeted me `%s'", + g_dbus_method_invocation_get_sender (g_dbus_method_invocation_get_for_gobject (G_OBJECT (frobnicator))), + greeting); + } + return response; +} + +/** + * my_frobnicator_hello_world_with_gerror: + * + * Like my_frobnicator_hello_world() but takes a #GError. + * + * Attributes: (gdbus.method HelloWorldWithGError) + */ +gchar * +my_frobnicator_hello_world_with_gerror (MyFrobnicator *frobnicator, + const gchar *greeting, + GError **error) +{ + gchar *response; + + g_return_val_if_fail (MY_IS_FROBNICATOR (frobnicator), NULL); + g_return_val_if_fail (greeting != NULL, NULL); + + response = NULL; + + if (g_strcmp0 (greeting, "fail") == 0) + { + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_FILE_NOT_FOUND, + "fail boat with GError!"); + } + else + { + response = g_strdup_printf ("Hey ya'll with GError - you greeted me `%s'", + greeting); + } + + return response; +} + +/** + * my_frobnicator_simple_types: + * @frobnicator: A #MyFrobnicator. + * @v_boolean: Value. + * @v_int16: Value. + * @v_uint16: Value. + * @v_int32: Value. + * @v_uint32: Value. + * @v_int64: Value. + * @v_uint64: Value. + * @v_int: Value. + * @v_uint: Value. + * @v_double: Value. + * @v_string: Value. + * @v_object_path: (gdbus.signature o) (foo.bar baz): Value. + * @v_signature: (gdbus.signature g): Value. + * @out_boolean: (out): Return location for value. + * @out_int16: (out): Return location for value. + * @out_uint16: (out): Return location for value. + * @out_int32: (out): Return location for value. + * @out_uint32: (out): Return location for value. + * @out_int64: (out): Return location for value. + * @out_uint64: (out): Return location for value. + * @out_int: (out): Return location for value. + * @out_uint: (out): Return location for value. + * @out_double: (out): Return location for value. + * @out_string: (out): Return location for value. + * @out_object_path: (out) (gdbus.signature o): Return location for value. + * @out_signature: (out) (gdbus.signature g): Return location for value. + * + * Method demonstrating simple types. + * + * Attributes: (gdbus.method SimpleTypes) + */ +void +my_frobnicator_simple_types (MyFrobnicator *frobnicator, + gboolean v_boolean, + gint16 v_int16, + guint16 v_uint16, + gint32 v_int32, + guint32 v_uint32, + gint64 v_int64, + guint64 v_uint64, + gint v_int, + guint v_uint, + gdouble v_double, + const gchar *v_string, + const gchar *v_object_path, + const gchar *v_signature, + gboolean *out_boolean, + gint16 *out_int16, + guint16 *out_uint16, + gint32 *out_int32, + guint32 *out_uint32, + gint64 *out_int64, + guint64 *out_uint64, + gint *out_int, + guint *out_uint, + gdouble *out_double, + gchar **out_string, + gchar **out_object_path, + gchar **out_signature) +{ + g_return_if_fail (MY_IS_FROBNICATOR (frobnicator)); + *out_boolean = !v_boolean; + *out_int16 = v_int16 + 100; + *out_uint16 = v_uint16 + 101; + *out_int32 = v_int32 + 102; + *out_uint32 = v_uint32 + 103; + *out_int64 = v_int64 + 104; + *out_uint64 = v_uint64 + 105; + *out_int = v_int + 106; + *out_uint = v_uint + 107; + *out_double = v_double + 108; + *out_string = g_strdup_printf ("You passed `%s'", v_string); + *out_object_path = g_strdup_printf ("/you/passed%s", v_object_path); + *out_signature = g_strdup_printf ("assgit%s", v_signature); +} + +/** + * my_frobnicator_strip: + * @frobnicator: A #MyFrobnicator. + * @str: (inout): A string that will be stripped using g_strstrip(). + * + * An example of a method with an inout parameter. + * + * Attributes: (gdbus.method Strip) + */ +void +my_frobnicator_strip (MyFrobnicator *frobnicator, + gchar *str) +{ + g_return_if_fail (MY_IS_FROBNICATOR (frobnicator)); + g_return_if_fail (str != NULL); + g_strstrip (str); +} + +/** + * my_frobnicator_return_const_str: + * @frobnicator: A #MyFrobnicator. + * @num: A number. + * + * An method that returns a const object. + * + * Returns: A constant string that should not be freed. + * + * Attributes: (gdbus.method ReturnConstStr) + */ +const gchar * +my_frobnicator_return_const_str (MyFrobnicator *frobnicator, + guint num) +{ + const gchar *ret; + switch (num) + { + case 0: + ret = "zero"; + break; + case 1: + ret = "one"; + break; + case 2: + ret = "two"; + break; + default: + ret = "bigger than two!"; + break; + } + return ret; +} + +/** + * my_frobnicator_poke_path: + * @frobnicator: A #MyFrobnicator + * @object_path: (gdbus.signature o) (foo.bar bat): An object path. + * + * Manipulate an object path. + * + * Returns: (gdbus.signature o): A new object path. Free with g_free(). + * + * Attributes: (gdbus.method PokePath) + */ +gchar * +my_frobnicator_poke_path (MyFrobnicator *frobnicator, + const gchar *object_path) +{ + return g_strdup_printf ("/another/object/path/here%s", object_path); +} + diff --git a/src/myfrobnicator.h b/src/myfrobnicator.h new file mode 100644 index 0000000..92686b8 --- /dev/null +++ b/src/myfrobnicator.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#ifndef __MY_FROBNICATOR_H__ +#define __MY_FROBNICATOR_H__ + +#include + +G_BEGIN_DECLS + +#define MY_TYPE_FROBNICATOR (my_frobnicator_get_type ()) +#define MY_FROBNICATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_FROBNICATOR, MyFrobnicator)) +#define MY_FROBNICATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MY_TYPE_FROBNICATOR, MyFrobnicatorClass)) +#define MY_FROBNICATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MY_TYPE_FROBNICATOR, MyFrobnicatorClass)) +#define MY_IS_FROBNICATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_FROBNICATOR)) +#define MY_IS_FROBNICATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MY_TYPE_FROBNICATOR)) + +typedef struct _MyFrobnicator MyFrobnicator; +typedef struct _MyFrobnicatorClass MyFrobnicatorClass; +typedef struct _MyFrobnicatorPrivate MyFrobnicatorPrivate; + +/** + * MyFrobnicator: + * + * The #MyFrobnicator structure contains only private data and + * should only be accessed using the provided API. + * + * Attributes: (gdbus.interface org.MyProject.Frobnicator) + */ +struct _MyFrobnicator +{ + /*< private >*/ + GObject parent_instance; + MyFrobnicatorPrivate *priv; +}; + +/** + * MyFrobnicatorClass: + * @closed: Signal class handler for the #MyFrobnicator::closed signal. + * + * Class structure for #MyFrobnicator. + */ +struct _MyFrobnicatorClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +GType my_frobnicator_get_type (void) G_GNUC_CONST; +MyFrobnicator *my_frobnicator_new (void); +gchar *my_frobnicator_hello_world (MyFrobnicator *frobnicator, + const gchar *greeting); +gchar *my_frobnicator_hello_world_with_gerror (MyFrobnicator *frobnicator, + const gchar *greeting, + GError **error); +void my_frobnicator_simple_types (MyFrobnicator *frobnicator, + gboolean v_boolean, + gint16 v_int16, + guint16 v_uint16, + gint32 v_int32, + guint32 v_uint32, + gint64 v_int64, + guint64 v_uint64, + gint v_int, + guint v_uint, + gdouble v_double, + const gchar *v_string, + const gchar *v_object_path, + const gchar *v_signature, + gboolean *out_boolean, + gint16 *out_int16, + guint16 *out_uint16, + gint32 *out_int32, + guint32 *out_uint32, + gint64 *out_int64, + guint64 *out_uint64, + gint *out_int, + guint *out_uint, + gdouble *out_double, + gchar **out_string, + gchar **out_object_path, + gchar **out_signature); +void my_frobnicator_strip (MyFrobnicator *frobnicator, + gchar *str); +const gchar *my_frobnicator_return_const_str (MyFrobnicator *frobnicator, + guint num); + +gchar *my_frobnicator_poke_path (MyFrobnicator *frobnicator, + const gchar *object_path); + + +G_END_DECLS + +#endif /* __MY_FROBNICATOR_H__ */ diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..1bdec4b --- /dev/null +++ b/src/test.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include "config.h" + +#include +#include "gdbusgi.h" + +#include + +#include "myfrobnicator.h" + +static GMainLoop *loop; +static MyFrobnicator *obj; + +static gboolean just_serve = FALSE; + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GError *error; + + error = NULL; + g_dbus_connection_register_gobject (connection, + G_OBJECT (obj), + "/org/My/Frobnicator", + NULL, /* user_data */ + NULL, /* GDestroynotify */ + &error); + g_assert_no_error (error); +} + +static void +run_test (GDBusConnection *connection) +{ + GError *error; + GVariant *ret; + gchar *s; + gchar *s2; + guint n; + + error = NULL; + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "HelloWorld", + g_variant_new ("(s)", "hey"), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (ret, "(&s)", &s); + s2 = g_strdup_printf ("Hey %s - you greeted me `hey'", + g_dbus_connection_get_unique_name (connection)); + g_assert_cmpstr (s, ==, s2); + g_free (s2); + g_variant_unref (ret); + + error = NULL; + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "HelloWorld", + g_variant_new ("(s)", "fail"), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.gtk.GDBus.UnmappedGError.Quark._g_2dio_2derror_2dquark.Code0: fail boat!"); + g_error_free (error); + g_assert (ret == NULL); + + error = NULL; + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "HelloWorldWithGError", + g_variant_new ("(s)", "hejsa"), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (ret, "(&s)", &s); + g_assert_cmpstr (s, ==, "Hey ya'll with GError - you greeted me `hejsa'"); + g_variant_unref (ret); + + error = NULL; + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "HelloWorldWithGError", + g_variant_new ("(s)", "fail"), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.FileNotFound: fail boat with GError!"); + g_error_free (error); + g_assert (ret == NULL); + + for (n = 0; n < 4; n++) + { + error = NULL; + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "ReturnConstStr", + g_variant_new ("(u)", n), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (ret, "(&s)", &s); + switch (n) + { + case 0: + g_assert_cmpstr (s, ==, "zero"); + break; + case 1: + g_assert_cmpstr (s, ==, "one"); + break; + case 2: + g_assert_cmpstr (s, ==, "two"); + break; + case 3: + g_assert_cmpstr (s, ==, "bigger than two!"); + break; + default: + g_assert_not_reached (); + } + g_variant_unref (ret); + } + + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "Strip", + g_variant_new ("(s)", " foobar "), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (ret, "(&s)", &s); + g_assert_cmpstr (s, ==, "foobar"); + g_variant_unref (ret); + + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "PokePath", + g_variant_new ("(o)", "/hells/yeahs"), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (ret, "(&o)", &s); + g_assert_cmpstr (s, ==, "/another/object/path/here/hells/yeahs"); + g_variant_unref (ret); + + + ret = g_dbus_connection_call_sync (connection, + "org.My.Service", + "/org/My/Frobnicator", + "org.MyProject.Frobnicator", + "SimpleTypes", + g_variant_new ("(bnqiuxtiudsog)", + TRUE, 10, 11, 12, 13, G_GINT64_CONSTANT (14), G_GUINT64_CONSTANT (15), 16, 17, 18.5, + "a string", "/the/path", "assgit"), + G_VARIANT_TYPE ("(bnqiuxtiudsog)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + { + gboolean v_boolean; + gint16 v_int16; + guint16 v_uint16; + gint32 v_int32; + guint32 v_uint32; + gint64 v_int64; + guint64 v_uint64; + gint v_int; + guint v_uint; + gdouble v_double; + const gchar *v_string; + const gchar *v_object_path; + const gchar *v_signature; + g_variant_get (ret, + "(bnqiuxtiud&s&o&g)", + &v_boolean, &v_int16, &v_uint16, &v_int32, &v_uint32, + &v_int64, &v_uint64, &v_int, &v_uint, &v_double, + &v_string, &v_object_path, &v_signature); + g_assert (!!v_boolean == !TRUE); + g_assert_cmpint (v_int16, ==, 10 + 100); + g_assert_cmpint (v_uint16, ==, 11 + 101); + g_assert_cmpint (v_int32, ==, 12 + 102); + g_assert_cmpint (v_uint32, ==, 13 + 103); + g_assert_cmpint (v_int64, ==, 14 + 104); + g_assert_cmpint (v_uint64, ==, 15 + 105); + g_assert_cmpint (v_int, ==, 16 + 106); + g_assert_cmpint (v_uint, ==, 17 + 107); + g_assert_cmpint (v_double, ==, 18.5 + 108); + } + g_variant_unref (ret); + +} + +static gpointer +run_test_thread_func (gpointer user_data) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GMainContext *thread_context; + GMainLoop *thread_loop; + + thread_context = g_main_context_new (); + thread_loop = g_main_loop_new (thread_context, FALSE); + g_main_context_push_thread_default (thread_context); + + run_test (connection); + + g_main_loop_unref (thread_loop); + g_main_context_unref (thread_context); + + g_main_loop_quit (loop); + return NULL; +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + if (just_serve) + return; + + /* Run test in thread to avoid deadlock */ + g_thread_create (run_test_thread_func, + connection, + TRUE, + NULL); /* GError** */ + g_main_loop_run (loop); + g_main_loop_quit (loop); +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + g_assert_not_reached (); +} + +static void +load_typelib (void) +{ + GMappedFile *mfile; + GTypelib *lib; + GError *error; + + error = NULL; + mfile = g_mapped_file_new ("My-1.0.typelib", + FALSE, + &error); + g_assert_no_error (error); + + lib = g_typelib_new_from_mapped_file (mfile); + g_irepository_load_typelib (g_irepository_get_default (), + lib, + 0, /* GIRepositoryLoadFlags */ + &error); + g_assert_no_error (error); +} + +gint +main (gint argc, gchar *argv[]) +{ + guint owner_id; + + g_type_init (); + + load_typelib (); + + loop = g_main_loop_new (NULL, FALSE); + + obj = my_frobnicator_new (); + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.My.Service", + G_BUS_NAME_OWNER_FLAGS_REPLACE | G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + if (argc > 1) + just_serve = TRUE; + + g_main_loop_run (loop); + + g_main_loop_unref (loop); + g_object_unref (obj); + + return 0; +} -- cgit v1.2.3