From: Xavier Claessens Date: Tue, 26 Feb 2008 13:31:02 +0000 (+0000) Subject: Import tools from telepathy-glib 0.7.3 and build a static libemp-extensions.la. X-Git-Url: https://git.0d.be/?p=empathy.git;a=commitdiff_plain;h=c3751daec7cca3e44abfed1db475cf0fc4703d66 Import tools from telepathy-glib 0.7.3 and build a static libemp-extensions.la. Link that library into libempathy (it's not actually used for anything at this point). Extensions currently built: ChannelHandler and StreamEngine. The namespacing convention used is emp_*, Emp*, EMP_* so it won't be included in the library ABI. svn path=/trunk/; revision=663 --- diff --git a/Makefile.am b/Makefile.am index 2d0d907f..14aa451e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = po data libempathy libempathy-gtk src docs +SUBDIRS = tools extensions po data libempathy libempathy-gtk src docs if HAVE_MEGAPHONE SUBDIRS += megaphone diff --git a/configure.ac b/configure.ac index ac012243..4d26cae4 100644 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ GCONF_REQUIRED=1.2.0 LIBGLADE_REQUIRED=2.0.0 LIBPANELAPPLET_REQUIRED=2.10.0 TELEPATHY_REQUIRED=0.3.1 -TELEPATHY_GLIB_REQUIRED=0.7.0 +TELEPATHY_GLIB_REQUIRED=0.7.3 MISSION_CONTROL_REQUIRED=4.53 # Uncomment that to build without deprecated symbols @@ -52,6 +52,7 @@ AC_ISC_POSIX AC_PROG_CC AC_HEADER_STDC AM_PROG_LIBTOOL +AM_PROG_MKDIR_P AM_PATH_GLIB_2_0 AC_PATH_XTRA IT_PROG_INTLTOOL([0.35.0]) @@ -63,6 +64,15 @@ AM_GCONF_SOURCE_2 GLIB_GENMARSHAL=`$PKG_CONFIG glib-2.0 --variable=glib_genmarshal` AC_SUBST(GLIB_GENMARSHAL) +AC_CHECK_PROGS([XSLTPROC], [xsltproc]) +if test -z "$XSLTPROC"; then + AC_MSG_ERROR([xsltproc (from libxslt) is required]) +fi +AC_CHECK_PROGS([PYTHON], [python2.3 python2.4 python2.5 python]) +if test -z "$PYTHON"; then + AC_MSG_ERROR([Python is required]) +fi + EMPATHY_ARG_VALGRIND # ----------------------------------------------------------- @@ -300,6 +310,7 @@ AC_OUTPUT([ data/Makefile data/empathy.desktop.in data/icons/Makefile + extensions/Makefile po/Makefile.in libempathy/Makefile libempathy/libempathy.pc @@ -321,4 +332,5 @@ AC_OUTPUT([ python/pyempathy/Makefile python/pyempathygtk/Makefile tests/Makefile + tools/Makefile ]) diff --git a/extensions/.gitignore b/extensions/.gitignore new file mode 100644 index 00000000..09d7dbf5 --- /dev/null +++ b/extensions/.gitignore @@ -0,0 +1,2 @@ +_gen +extensions.html diff --git a/extensions/Channel_Handler.xml b/extensions/Channel_Handler.xml new file mode 100644 index 00000000..80233e97 --- /dev/null +++ b/extensions/Channel_Handler.xml @@ -0,0 +1,62 @@ + + + Copyright (C) 2007 Collabora Limited + +

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.1 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 +Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

+
+ + + + + + The bus name of the connection and channel + + + + + The object-path of the connection that owns the channel + + + + + The channel type + + + + + The object-path of the channel + + + + The type of the handle that the channel communicates + with, or 0 if there is no associated handle + + + The handle that the channel communicates with, + or 0 if there is no associated handle + + + Called when a channel handler should handle a new channel. + + + + +

An interface exported by client applications which are able to + handle incoming channels.

+
+
+
+ + diff --git a/extensions/Makefile.am b/extensions/Makefile.am new file mode 100644 index 00000000..ccf7bb59 --- /dev/null +++ b/extensions/Makefile.am @@ -0,0 +1,130 @@ +tools_dir = $(top_srcdir)/tools + +AM_CFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(WARN_CFLAGS) \ + $(LIBEMPATHY_CFLAGS) + +EXTRA_DIST = \ + all.xml \ + misc.xml \ + Channel_Handler.xml \ + Stream_Engine.xml + +noinst_LTLIBRARIES = libemp-extensions.la + +libemp_extensions_la_LIBADD = \ + $(TELEPATHY_GLIB_LIBS) + +# The client-specific parts are built into a separate .o file, so the linker +# can discard them when linking services. The service-specific parts are +# in svc-*.c, so we don't need an extensions-svc.c. +libemp_extensions_la_SOURCES = \ + extensions.c \ + extensions-cli.c \ + extensions.h + +nodist_libemp_extensions_la_SOURCES = \ + _gen/signals-marshal.c \ + _gen/signals-marshal.h \ + _gen/signals-marshal.list \ + _gen/register-dbus-glib-marshallers-body.h \ + _gen/enums.h \ + _gen/gtypes.h \ + _gen/gtypes-body.h \ + _gen/interfaces.h \ + _gen/interfaces-body.h \ + _gen/cli-misc.h \ + _gen/cli-misc-body.h \ + _gen/svc-misc.h \ + _gen/svc-misc.c + +BUILT_SOURCES = \ + _gen/all.xml \ + _gen/misc.xml \ + $(nodist_libemp_extensions_la_SOURCES) \ + extensions.html + +CLEANFILES = $(BUILT_SOURCES) + +XSLTPROCFLAGS = --nonet --novalid + +# Generated files which can be generated for all categories simultaneously + +_gen/%.xml: %.xml $(wildcard *.xml) + $(mkdir_p) _gen + $(XSLTPROC) $(XSLTPROCFLAGS) --xinclude $(tools_dir)/identity.xsl \ + $< > $@ + +extensions.html: _gen/all.xml $(tools_dir)/doc-generator.xsl + $(XSLTPROC) $(XSLTPROCFLAGS) \ + $(tools_dir)/doc-generator.xsl \ + $< > $@ + +_gen/gtypes.h _gen/gtypes-body.h: _gen/all.xml \ + $(top_srcdir)/tools/glib-gtypes-generator.py + $(PYTHON) $(top_srcdir)/tools/glib-gtypes-generator.py \ + $< _gen/gtypes Emp + +_gen/signals-marshal.list: _gen/all.xml \ + $(tools_dir)/glib-signals-marshal-gen.py + $(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py $< > $@ + +_gen/signals-marshal.h: _gen/signals-marshal.list + $(GLIB_GENMARSHAL) --header --prefix=_emp_ext_marshal $< > $@ + +_gen/signals-marshal.c: _gen/signals-marshal.list + ( \ + echo '#include "signals-marshal.h"' && \ + $(GLIB_GENMARSHAL) --body --prefix=_emp_ext_marshal $< \ + ) >> $@ + +_gen/register-dbus-glib-marshallers-body.h: _gen/all.xml \ + $(tools_dir)/glib-client-marshaller-gen.py + $(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \ + _emp_ext > $@ + +_gen/enums.h: _gen/all.xml $(tools_dir)/c-constants-generator.xsl + $(XSLTPROC) $(XSLTPROCFLAGS) \ + --stringparam mixed-case-prefix Emp \ + $(tools_dir)/c-constants-generator.xsl \ + $< > $@ + +_gen/interfaces.h: _gen/all.xml \ + $(tools_dir)/glib-interfaces-generator.xsl \ + $(tools_dir)/c-interfaces-generator.xsl + $(XSLTPROC) $(XSLTPROCFLAGS) \ + --stringparam mixed-case-prefix Emp \ + $(tools_dir)/glib-interfaces-generator.xsl \ + $< > $@ + +_gen/interfaces-body.h: _gen/all.xml \ + $(tools_dir)/glib-interfaces-body-generator.xsl \ + $(tools_dir)/c-interfaces-generator.xsl + $(XSLTPROC) $(XSLTPROCFLAGS) \ + --stringparam mixed-case-prefix Emp \ + $(tools_dir)/glib-interfaces-body-generator.xsl \ + $< > $@ + +# Generated files which must be generated per "category". Each TpProxy +# subclass you want to use with --subclass will need to have its own category, +# although you can subdivide further if you want. + +_gen/cli-misc-body.h _gen/cli-misc.h: _gen/misc.xml \ + $(tools_dir)/glib-client-gen.py + $(PYTHON) $(tools_dir)/glib-client-gen.py \ + --group=misc \ + --iface-quark-prefix=EMP_IFACE_QUARK \ + $< Emp_Cli _gen/cli-misc + +_gen/svc-misc.c _gen/svc-misc.h: _gen/misc.xml \ + $(tools_dir)/glib-ginterface-gen.py + $(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ + --filename=_gen/svc-misc \ + --signal-marshal-prefix=_emp_ext \ + --include='' \ + --include='"_gen/signals-marshal.h"' \ + --not-implemented-func='tp_dbus_g_method_return_not_implemented' \ + --allow-unstable \ + $< Emp_Svc_ diff --git a/extensions/Stream_Engine.xml b/extensions/Stream_Engine.xml new file mode 100644 index 00000000..0303a516 --- /dev/null +++ b/extensions/Stream_Engine.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/all.xml b/extensions/all.xml new file mode 100644 index 00000000..2a0e109e --- /dev/null +++ b/extensions/all.xml @@ -0,0 +1,14 @@ + + +Extensions for Empathy + + + + + + + + diff --git a/extensions/extensions-cli.c b/extensions/extensions-cli.c new file mode 100644 index 00000000..98a17447 --- /dev/null +++ b/extensions/extensions-cli.c @@ -0,0 +1,19 @@ +#include "extensions.h" + +#include + +static void _emp_ext_register_dbus_glib_marshallers (void); + +/* include auto-generated stubs for client-specific code */ +#include "_gen/signals-marshal.h" +#include "_gen/cli-misc-body.h" +#include "_gen/register-dbus-glib-marshallers-body.h" + +void +emp_cli_init (void) +{ + _emp_ext_register_dbus_glib_marshallers (); + + tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_PROXY, + emp_cli_misc_add_signals); +} diff --git a/extensions/extensions.c b/extensions/extensions.c new file mode 100644 index 00000000..eeda4623 --- /dev/null +++ b/extensions/extensions.c @@ -0,0 +1,6 @@ +#include "extensions.h" + +/* include auto-generated stubs for things common to service and client */ +#include "_gen/gtypes-body.h" +#include "_gen/interfaces-body.h" +#include "_gen/signals-marshal.h" diff --git a/extensions/extensions.h b/extensions/extensions.h new file mode 100644 index 00000000..d97dc830 --- /dev/null +++ b/extensions/extensions.h @@ -0,0 +1,20 @@ +#ifndef __EMP_EXTENSIONS_H__ +#define __EMP_EXTENSIONS_H__ + +#include +#include + +#include "extensions/_gen/enums.h" +#include "extensions/_gen/cli-misc.h" +#include "extensions/_gen/svc-misc.h" + +G_BEGIN_DECLS + +#include "extensions/_gen/gtypes.h" +#include "extensions/_gen/interfaces.h" + +void emp_cli_init (void); + +G_END_DECLS + +#endif diff --git a/extensions/misc.xml b/extensions/misc.xml new file mode 100644 index 00000000..070ae85c --- /dev/null +++ b/extensions/misc.xml @@ -0,0 +1,10 @@ + + +Channel extensions for Empathy + + + + + diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am index 90b3aa89..adf9b7d5 100644 --- a/libempathy/Makefile.am +++ b/libempathy/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = \ -I. \ -I$(top_srcdir) \ + -I$(top_builddir) \ -DDATADIR=\""$(datadir)"\" \ $(LIBEMPATHY_CFLAGS) \ $(WARN_CFLAGS) @@ -51,6 +52,7 @@ nodist_libempathy_la_SOURCES =\ $(BUILT_SOURCES) libempathy_la_LIBADD = \ + $(top_builddir)/extensions/libemp-extensions.la \ $(LIBEMPATHY_LIBS) libempathy_la_LDFLAGS = \ diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 00000000..48bd3a88 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1 @@ +.py[co] diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 00000000..4bf514f1 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,41 @@ +EXTRA_DIST = \ + c-constants-generator.xsl \ + check-coding-style.mk \ + check-c-style.sh \ + check-misc.sh \ + check-whitespace.sh \ + c-interfaces-generator.xsl \ + doc-generator.xsl \ + glib-client-gen.py \ + glib-client-marshaller-gen.py \ + glib-errors-enum-body.xsl \ + glib-errors-enum-header.xsl \ + glib-interfaces-generator.xsl \ + glib-interfaces-body-generator.xsl \ + glib-ginterface-gen.py \ + glib-gtypes-generator.py \ + glib-signals-marshal-gen.py \ + identity.xsl \ + libglibcodegen.py + +CLEANFILES = libglibcodegen.pyc libglibcodegen.pyo $(noinst_SCRIPTS) + +all: $(EXTRA_DIST) + +glib-client-marshaller-gen.py: libglibcodegen.py + touch $@ +glib-ginterface-gen.py: libglibcodegen.py + touch $@ +glib-gtypes-generator.py: libglibcodegen.py + touch $@ +glib-signals-marshal-gen.py: libglibcodegen.py + touch $@ + +TELEPATHY_GLIB_SRCDIR = $(top_srcdir)/../telepathy-glib +maintainer-update-from-telepathy-glib: + set -e && cd $(srcdir) && \ + for x in $(EXTRA_DIST); do \ + if test -f $(TELEPATHY_GLIB_SRCDIR)/tools/$$x; then \ + cp $(TELEPATHY_GLIB_SRCDIR)/tools/$$x $$x; \ + fi; \ + done diff --git a/tools/c-constants-generator.xsl b/tools/c-constants-generator.xsl new file mode 100644 index 00000000..18b2e495 --- /dev/null +++ b/tools/c-constants-generator.xsl @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /** + * + + : + + + + * + + * <![CDATA[ + + ]]> + * + + * Bitfield/set of flags generated from the Telepathy specification. + */ + typedef enum { + + + + } + + ; + + + + + + + + * @ + + : <![CDATA[ + + ]]> + + + + + * @ + + : <![CDATA[ + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s + + + + /** + * + + : + + + + * + + * <![CDATA[ + + ]]> + * + + * Bitfield/set of flags generated from the Telepathy specification. + */ + typedef enum { + + + + } + + ; + + /** + * NUM_ + + : + * + * 1 higher than the highest valid value of # + + . + */ + #define NUM_ + + ( + + +1) + + + + + + + + + + + + + + + + + + + + Flag name + + != suffix + + + + + + + = + + , + + + + + + + + + + + + + + + + + + + Enumvalue name + + != suffix + + + + + + + + Enum values must be in ascending numeric order, but + + is less than the previous value + + + + + + = + + , + + + + tp:flag found outside tp:flags + + + + tp:enumvalue found outside tp:enum + + + + + + + + mixed-case-prefix param must be set + + + + /* Generated from + + + , version + + + + + + + + + + + + + */ + + #ifdef __cplusplus + extern "C" { + #endif + + + + #ifdef __cplusplus + } + #endif + + + + + diff --git a/tools/c-interfaces-generator.xsl b/tools/c-interfaces-generator.xsl new file mode 100644 index 00000000..f965a705 --- /dev/null +++ b/tools/c-interfaces-generator.xsl @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + /** * + + _IFACE_ + + : * * The interface name " + + " */ #define + + _IFACE_ + + \ " + + " + + + + + + + + mixed-case-prefix param must be set + + + + /* Generated from: + + + version + + + + + + + + + + + */ + + + + + + diff --git a/tools/check-c-style.sh b/tools/check-c-style.sh new file mode 100644 index 00000000..357fdb36 --- /dev/null +++ b/tools/check-c-style.sh @@ -0,0 +1,36 @@ +#!/bin/sh +fail=0 + +( . "${tools_dir}"/check-misc.sh ) || fail=$? + +if grep -n '^ *GError *\*[[:alpha:]_][[:alnum:]_]* *;' "$@" +then + echo "^^^ The above files contain uninitialized GError*s - they should be" + echo " initialized to NULL" + fail=1 +fi + +# The first regex finds function calls like foo() (as opposed to foo ()). +# It attempts to ignore string constants (may cause false negatives). +# The second and third ignore block comments (gtkdoc uses foo() as markup). +# The fourth ignores cpp so you can +# #define foo(bar) (_real_foo (__FUNC__, bar)) (cpp insists on foo() style). +if grep -n '^[^"]*[[:lower:]](' "$@" \ + | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *\*' \ + | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: */\*' \ + | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *#' +then + echo "^^^ Our coding style is to use function calls like foo (), not foo()" + fail=1 +fi + +if test -n "$CHECK_FOR_LONG_LINES" +then + if egrep -n '.{80,}' "$@" + then + echo "^^^ The above files contain long lines" + fail=1 + fi +fi + +exit $fail diff --git a/tools/check-coding-style.mk b/tools/check-coding-style.mk new file mode 100644 index 00000000..1499f2f0 --- /dev/null +++ b/tools/check-coding-style.mk @@ -0,0 +1,17 @@ +check-local:: + @fail=0; \ + if test -n "$(check_misc_sources)"; then \ + tools_dir=$(top_srcdir)/tools \ + sh $(top_srcdir)/tools/check-misc.sh \ + $(check_misc_sources) || fail=1; \ + fi; \ + if test -n "$(check_c_sources)"; then \ + tools_dir=$(top_srcdir)/tools \ + sh $(top_srcdir)/tools/check-c-style.sh \ + $(check_c_sources) || fail=1; \ + fi;\ + if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ + exit "$$fail";\ + else \ + exit 0;\ + fi diff --git a/tools/check-misc.sh b/tools/check-misc.sh new file mode 100644 index 00000000..89e8e871 --- /dev/null +++ b/tools/check-misc.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +fail=0 + +( . "${tools_dir}"/check-whitespace.sh ) || fail=$? + +if egrep '(Free\s*Software\s*Foundation.*02139|02111-1307)' "$@" +then + echo "^^^ The above files contain the FSF's old address in GPL headers" + fail=1 +fi + +exit $fail diff --git a/tools/check-whitespace.sh b/tools/check-whitespace.sh new file mode 100644 index 00000000..53483312 --- /dev/null +++ b/tools/check-whitespace.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +fail=0 + +if grep -n ' $' "$@" +then + echo "^^^ The above files contain unwanted trailing spaces" + fail=1 +fi + +if grep -n ' ' "$@" +then + echo "^^^ The above files contain tabs" + fail=1 +fi + +exit $fail diff --git a/tools/doc-generator.xsl b/tools/doc-generator.xsl new file mode 100644 index 00000000..83b42dc5 --- /dev/null +++ b/tools/doc-generator.xsl @@ -0,0 +1,689 @@ + + + + + + + + + + + + + + + + +

Errors

+ +
+ + +

Generic types

+ +
+ + + +

Simple types

+ +
+ + +

Enumerated types:

+ +
+ + +

Sets of flags:

+ +
+ + +

Structure types

+ +
+ + +

Mapping types

+ +
+ + +

Types defined elsewhere

+
+
+
+ + +

+ +
+ + +
+ +
+
+ +
+ +
+
+ + + + + +

+ + +

+ This interface is + and is likely to cause havoc to your API/ABI if bindings are generated. + Don't include it in libraries that care about compatibility. +

+
+ + +

Implementations of this interface must also implement:

+
    + +
  • +
    +
+
+ + + + + +

Methods:

+ +
+ +

Interface has no methods.

+
+
+ + + +

Signals:

+ +
+ +

Interface has no signals.

+
+
+ + + +

Properties:

+
+ +
+
+ +

Interface has no properties.

+
+
+ + + +
+ + +

+ + + +

+ +
+ + + + + + + + + + + +
=
+ + +
+
+ +
(Undocumented)
+
+
+
+
+
+ + +

+ + + +

+ +
+ + + + + + + + + + + +
=
+ + +
+
+ +
(Undocumented)
+
+
+
+
+
+ + +
+ + - + + +
+
+ +
+
+ + +
+

+ + + - a{ + + + : + + + + } +

+
+ +
+
+

Members

+
+ +
+
+
+
+ + + + + - + + + +
+

+ + + - +

+
+ +
+
+
+ + +
+
+ + + - +
+
Defined by:
+
+
+ + + - ( + + , + ) + + + + - a{ + + + } + + + +
+

+ + + - ( + + + : + + , + + ) +

+
+ +
+ + +

In bindings that need a separate name, arrays of + should be called + .

+
+ +

Arrays of don't generally + make sense.

+
+
+
+

Members

+
+ +
+
+
+
+ + +
+

+ + + ( + + : + , + + ) → + + + + + , + + + nothing + +

+
+ +
+ + +
+

Parameters

+
+ +
+
+
+ + +
+

Returns

+
+ +
+
+
+ + +
+

Possible errors

+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + ERR: Unable to find type ' + + ' + + + + () + + + + +
+ - + + +
+
+ + + + + + (undocumented) + + +
+
+ + +
+ - + + +
+
+ +
+
+ + +
+ + - + + + +
+
+ +
+
+ + +
+ +
+
+ + + + + + + (generic description) + + + (Undocumented.) + + +
+
+ + +
+

+ + + ( + + : + , + + )

+
+ +
+ + +
+

Parameters

+
+ +
+
+
+
+
+ + + + + + + + <xsl:value-of select="tp:title"/> + <xsl:if test="tp:version"> + <xsl:text> version </xsl:text> + <xsl:value-of select="tp:version"/> + </xsl:if> + + + + +

+ +

+ +

Version

+
+ + + + +

Interfaces

+
    + +
  • +
    +
+ + + + + +

Index

+

Index of interfaces

+
    + +
  • +
    +
+

Index of types

+
    + + +
  • + + + + + + +
  • +
    +
+ + +
+ +
+ + diff --git a/tools/glib-client-gen.py b/tools/glib-client-gen.py new file mode 100644 index 00000000..4ff78cd0 --- /dev/null +++ b/tools/glib-client-gen.py @@ -0,0 +1,1126 @@ +#!/usr/bin/python + +# glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool" +# +# Generate GLib client wrappers from the Telepathy specification. +# The master copy of this program is in the telepathy-glib repository - +# please make any changes there. +# +# Copyright (C) 2006-2008 Collabora Ltd. +# +# 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import sys +import os.path +import xml.dom.minidom +from getopt import gnu_getopt + +from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ + camelcase_to_lower, get_docstring, xml_escape + + +NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + +class Generator(object): + + def __init__(self, dom, prefix, basename, opts): + self.dom = dom + self.__header = [] + self.__body = [] + + self.prefix_lc = prefix.lower() + self.prefix_uc = prefix.upper() + self.prefix_mc = prefix.replace('_', '') + self.basename = basename + self.group = opts.get('--group', None) + self.iface_quark_prefix = opts.get('--iface-quark-prefix', None) + self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *' + self.proxy_arg = opts.get('--subclass', 'void') + ' *' + self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY') + self.proxy_doc = ('A #%s or subclass' + % opts.get('--subclass', 'TpProxy')) + if self.proxy_arg == 'void *': + self.proxy_arg = 'gpointer ' + + def h(self, s): + self.__header.append(s) + + def b(self, s): + self.__body.append(s) + + def get_iface_quark(self): + assert self.iface_dbus is not None + assert self.iface_uc is not None + if self.iface_quark_prefix is None: + return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus + else: + return '%s_%s' % (self.iface_quark_prefix, self.iface_uc) + + def do_signal(self, iface, signal): + iface_lc = iface.lower() + + member = signal.getAttribute('name') + member_lc = camelcase_to_lower(member) + member_uc = member_lc.upper() + + arg_count = 0 + args = [] + out_args = [] + + for arg in signal.getElementsByTagName('arg'): + name = arg.getAttribute('name') + type = arg.getAttribute('type') + tp_type = arg.getAttribute('tp:type') + + if not name: + name = 'arg%u' % arg_count + arg_count += 1 + else: + name = 'arg_%s' % name + + info = type_to_gtype(type) + args.append((name, info, tp_type, arg)) + + callback_name = ('%s_%s_signal_callback_%s' + % (self.prefix_lc, iface_lc, member_lc)) + collect_name = ('_%s_%s_collect_args_of_%s' + % (self.prefix_lc, iface_lc, member_lc)) + invoke_name = ('_%s_%s_invoke_callback_for_%s' + % (self.prefix_lc, iface_lc, member_lc)) + + # Example: + # + # typedef void (*tp_cli_connection_signal_callback_new_channel) + # (TpConnection *proxy, const gchar *arg_object_path, + # const gchar *arg_channel_type, guint arg_handle_type, + # guint arg_handle, gboolean arg_suppress_handler, + # gpointer user_data, GObject *weak_object); + + self.b('/**') + self.b(' * %s:' % callback_name) + self.b(' * @proxy: The proxy on which %s_%s_connect_to_%s ()' + % (self.prefix_lc, iface_lc, member_lc)) + self.b(' * was called') + + for arg in args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' * @%s: %s' % (name, + xml_escape(get_docstring(elt) or '(Undocumented)'))) + + self.b(' * @user_data: User-supplied data') + self.b(' * @weak_object: User-supplied weakly referenced object') + self.b(' *') + self.b(' * Represents the signature of a callback for the signal %s.' + % member) + self.b(' */') + self.h('typedef void (*%s) (%sproxy,' + % (callback_name, self.proxy_cls)) + + for arg in args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.h(' %s%s%s,' % (const, ctype, name)) + + self.h(' gpointer user_data, GObject *weak_object);') + + if args: + self.b('static void') + self.b('%s (DBusGProxy *proxy,' % collect_name) + + for arg in args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.b(' %s%s%s,' % (const, ctype, name)) + + self.b(' TpProxySignalConnection *sc)') + self.b('{') + self.b(' GValueArray *args = g_value_array_new (%d);' % len(args)) + self.b(' GValue blank = { 0 };') + self.b(' guint i;') + self.b('') + self.b(' g_value_init (&blank, G_TYPE_INT);') + self.b('') + self.b(' for (i = 0; i < %d; i++)' % len(args)) + self.b(' g_value_array_append (args, &blank);') + self.b('') + + for i, arg in enumerate(args): + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' g_value_unset (args->values + %d);' % i) + self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) + + if gtype == 'G_TYPE_STRING': + self.b(' g_value_set_string (args->values + %d, %s);' + % (i, name)) + elif marshaller == 'BOXED': + self.b(' g_value_set_boxed (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_UCHAR': + self.b(' g_value_set_uchar (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_BOOLEAN': + self.b(' g_value_set_boolean (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_INT': + self.b(' g_value_set_int (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_UINT': + self.b(' g_value_set_uint (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_INT64': + self.b(' g_value_set_int (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_UINT64': + self.b(' g_value_set_uint (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_DOUBLE': + self.b(' g_value_set_double (args->values + %d, %s);' + % (i, name)) + else: + assert False, ("Don't know how to put %s in a GValue" + % gtype) + self.b('') + + self.b(' tp_proxy_signal_connection_v0_take_results (sc, args);') + self.b('}') + + self.b('static void') + self.b('%s (TpProxy *tpproxy,' % invoke_name) + self.b(' GError *error,') + self.b(' GValueArray *args,') + self.b(' GCallback generic_callback,') + self.b(' gpointer user_data,') + self.b(' GObject *weak_object)') + self.b('{') + self.b(' %s callback =' % callback_name) + self.b(' (%s) generic_callback;' % callback_name) + self.b('') + self.b(' if (callback != NULL)') + self.b(' callback (g_object_ref (tpproxy),') + + # FIXME: factor out into a function + for i, arg in enumerate(args): + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + if marshaller == 'BOXED': + self.b(' g_value_get_boxed (args->values + %d),' % i) + elif gtype == 'G_TYPE_STRING': + self.b(' g_value_get_string (args->values + %d),' % i) + elif gtype == 'G_TYPE_UCHAR': + self.b(' g_value_get_uchar (args->values + %d),' % i) + elif gtype == 'G_TYPE_BOOLEAN': + self.b(' g_value_get_boolean (args->values + %d),' % i) + elif gtype == 'G_TYPE_UINT': + self.b(' g_value_get_uint (args->values + %d),' % i) + elif gtype == 'G_TYPE_INT': + self.b(' g_value_get_int (args->values + %d),' % i) + elif gtype == 'G_TYPE_UINT64': + self.b(' g_value_get_uint64 (args->values + %d),' % i) + elif gtype == 'G_TYPE_INT64': + self.b(' g_value_get_int64 (args->values + %d),' % i) + elif gtype == 'G_TYPE_DOUBLE': + self.b(' g_value_get_double (args->values + %d),' % i) + else: + assert False, "Don't know how to get %s from a GValue" % gtype + + self.b(' user_data,') + self.b(' weak_object);') + self.b('') + + if len(args) > 0: + self.b(' g_value_array_free (args);') + else: + self.b(' if (args != NULL)') + self.b(' g_value_array_free (args);') + self.b('') + + self.b(' g_object_unref (tpproxy);') + self.b('}') + + # Example: + # + # TpProxySignalConnection * + # tp_cli_connection_connect_to_new_channel + # (TpConnection *proxy, + # tp_cli_connection_signal_callback_new_channel callback, + # gpointer user_data, + # GDestroyNotify destroy); + # + # destroy is invoked when the signal becomes disconnected. This + # is either because the signal has been disconnected explicitly + # by the user, because the TpProxy has become invalid and + # emitted the 'invalidated' signal, or because the weakly referenced + # object has gone away. + + self.b('/**') + self.b(' * %s_%s_connect_to_%s:' + % (self.prefix_lc, iface_lc, member_lc)) + self.b(' * @proxy: %s' % self.proxy_doc) + self.b(' * @callback: Callback to be called when the signal is') + self.b(' * received') + self.b(' * @user_data: User-supplied data for the callback') + self.b(' * @destroy: Destructor for the user-supplied data, which') + self.b(' * will be called when this signal is disconnected, or') + self.b(' * before this function returns %NULL') + self.b(' * @weak_object: A #GObject which will be weakly referenced; ') + self.b(' * if it is destroyed, this callback will automatically be') + self.b(' * disconnected') + self.b(' * @error: If not %NULL, used to raise an error if %NULL is') + self.b(' * returned') + self.b(' *') + self.b(' * Connect a handler to the signal %s.' % member) + self.b(' *') + self.b(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)')) + self.b(' *') + self.b(' * Returns: a #TpProxySignalConnection containing all of the') + self.b(' * above, which can be used to disconnect the signal; or') + self.b(' * %NULL if the proxy does not have the desired interface') + self.b(' * or has become invalid.') + self.b(' */') + self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,' + % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) + self.h(' %s callback,' % callback_name) + self.h(' gpointer user_data,') + self.h(' GDestroyNotify destroy,') + self.h(' GObject *weak_object,') + self.h(' GError **error);') + + self.b('TpProxySignalConnection *') + self.b('%s_%s_connect_to_%s (%sproxy,' + % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) + self.b(' %s callback,' % callback_name) + self.b(' gpointer user_data,') + self.b(' GDestroyNotify destroy,') + self.b(' GObject *weak_object,') + self.b(' GError **error)') + self.b('{') + self.b(' GType expected_types[%d] = {' % (len(args) + 1)) + + for arg in args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' %s,' % gtype) + + self.b(' G_TYPE_INVALID };') + self.b('') + self.b(' g_return_val_if_fail (%s (proxy), NULL);' + % self.proxy_assert) + self.b(' g_return_val_if_fail (callback != NULL, NULL);') + self.b('') + self.b(' return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,') + self.b(' %s, \"%s\",' % (self.get_iface_quark(), member)) + self.b(' expected_types,') + + if args: + self.b(' G_CALLBACK (%s),' % collect_name) + else: + self.b(' NULL, /* no args => no collector function */') + + self.b(' %s,' % invoke_name) + self.b(' G_CALLBACK (callback), user_data, destroy,') + self.b(' weak_object, error);') + self.b('}') + self.b('') + + self.h('') + + def do_method(self, iface, method): + iface_lc = iface.lower() + + member = method.getAttribute('name') + member_lc = camelcase_to_lower(member) + member_uc = member_lc.upper() + + in_count = 0 + ret_count = 0 + in_args = [] + out_args = [] + + for arg in method.getElementsByTagName('arg'): + name = arg.getAttribute('name') + direction = arg.getAttribute('direction') + type = arg.getAttribute('type') + tp_type = arg.getAttribute('tp:type') + + if direction != 'out': + if not name: + name = 'in%u' % in_count + in_count += 1 + else: + name = 'in_%s' % name + else: + if not name: + name = 'out%u' % ret_count + ret_count += 1 + else: + name = 'out_%s' % name + + info = type_to_gtype(type) + if direction != 'out': + in_args.append((name, info, tp_type, arg)) + else: + out_args.append((name, info, tp_type, arg)) + + # Async reply callback type + + # Example: + # void (*tp_cli_properties_interface_callback_for_get_properties) + # (TpProxy *proxy, + # const GPtrArray *out0, + # const GError *error, + # gpointer user_data, + # GObject *weak_object); + + self.b('/**') + self.b(' * %s_%s_callback_for_%s:' + % (self.prefix_lc, iface_lc, member_lc)) + self.b(' * @proxy: the proxy on which the call was made') + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' * @%s: Used to return an \'out\' argument if @error is ' + '%%NULL: %s' + % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) + + self.b(' * @error: %NULL on success, or an error on failure') + self.b(' * @user_data: user-supplied data') + self.b(' * @weak_object: user-supplied object') + self.b(' *') + self.b(' * Signature of the callback called when a %s method call' + % member) + self.b(' * succeeds or fails.') + self.b(' */') + + callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc, + member_lc) + + self.h('typedef void (*%s) (%sproxy,' + % (callback_name, self.proxy_cls)) + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + const = pointer and 'const ' or '' + + self.h(' %s%s%s,' % (const, ctype, name)) + + self.h(' const GError *error, gpointer user_data,') + self.h(' GObject *weak_object);') + self.h('') + + # Async callback implementation + + invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc, + iface_lc, + member_lc) + + collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, + iface_lc, + member_lc) + + # The callback called by dbus-glib; this ends the call and collects + # the results into a GValueArray. + self.b('static void') + self.b('%s (DBusGProxy *proxy,' % collect_callback) + self.b(' DBusGProxyCall *call,') + self.b(' gpointer user_data)') + self.b('{') + self.b(' GError *error = NULL;') + + if len(out_args) > 0: + self.b(' GValueArray *args;') + self.b(' GValue blank = { 0 };') + self.b(' guint i;') + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + # "We handle variants specially; the caller is expected to + # have already allocated storage for them". Thanks, + # dbus-glib... + if gtype == 'G_TYPE_VALUE': + self.b(' GValue *%s = g_new0 (GValue, 1);' % name) + else: + self.b(' %s%s;' % (ctype, name)) + + self.b('') + self.b(' dbus_g_proxy_end_call (proxy, call, &error,') + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + if gtype == 'G_TYPE_VALUE': + self.b(' %s, %s,' % (gtype, name)) + else: + self.b(' %s, &%s,' % (gtype, name)) + + self.b(' G_TYPE_INVALID);') + + if len(out_args) == 0: + self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,' + 'NULL);') + else: + self.b('') + self.b(' if (error != NULL)') + self.b(' {') + self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,') + self.b(' NULL);') + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + if gtype == 'G_TYPE_VALUE': + self.b(' g_free (%s);' % name) + + self.b(' return;') + self.b(' }') + self.b('') + self.b(' args = g_value_array_new (%d);' % len(out_args)) + self.b(' g_value_init (&blank, G_TYPE_INT);') + self.b('') + self.b(' for (i = 0; i < %d; i++)' % len(out_args)) + self.b(' g_value_array_append (args, &blank);') + + for i, arg in enumerate(out_args): + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b('') + self.b(' g_value_unset (args->values + %d);' % i) + self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) + + if gtype == 'G_TYPE_STRING': + self.b(' g_value_take_string (args->values + %d, %s);' + % (i, name)) + elif marshaller == 'BOXED': + self.b(' g_value_take_boxed (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_UCHAR': + self.b(' g_value_set_uchar (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_BOOLEAN': + self.b(' g_value_set_boolean (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_INT': + self.b(' g_value_set_int (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_UINT': + self.b(' g_value_set_uint (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_INT64': + self.b(' g_value_set_int (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_UINT64': + self.b(' g_value_set_uint (args->values + %d, %s);' + % (i, name)) + elif gtype == 'G_TYPE_DOUBLE': + self.b(' g_value_set_double (args->values + %d, %s);' + % (i, name)) + else: + assert False, ("Don't know how to put %s in a GValue" + % gtype) + + self.b(' tp_proxy_pending_call_v0_take_results (user_data, ' + 'NULL, args);') + + self.b('}') + + self.b('static void') + self.b('%s (TpProxy *self,' % invoke_callback) + self.b(' GError *error,') + self.b(' GValueArray *args,') + self.b(' GCallback generic_callback,') + self.b(' gpointer user_data,') + self.b(' GObject *weak_object)') + self.b('{') + self.b(' %s callback = (%s) generic_callback;' + % (callback_name, callback_name)) + self.b('') + self.b(' if (error != NULL)') + self.b(' {') + self.b(' callback ((%s) self,' % self.proxy_cls) + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + if marshaller == 'BOXED' or pointer: + self.b(' NULL,') + elif gtype == 'G_TYPE_DOUBLE': + self.b(' 0.0,') + else: + self.b(' 0,') + + self.b(' error, user_data, weak_object);') + self.b(' g_error_free (error);') + self.b(' return;') + self.b(' }') + + self.b(' callback ((%s) self,' % self.proxy_cls) + + # FIXME: factor out into a function + for i, arg in enumerate(out_args): + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + if marshaller == 'BOXED': + self.b(' g_value_get_boxed (args->values + %d),' % i) + elif gtype == 'G_TYPE_STRING': + self.b(' g_value_get_string (args->values + %d),' % i) + elif gtype == 'G_TYPE_UCHAR': + self.b(' g_value_get_uchar (args->values + %d),' % i) + elif gtype == 'G_TYPE_BOOLEAN': + self.b(' g_value_get_boolean (args->values + %d),' % i) + elif gtype == 'G_TYPE_UINT': + self.b(' g_value_get_uint (args->values + %d),' % i) + elif gtype == 'G_TYPE_INT': + self.b(' g_value_get_int (args->values + %d),' % i) + elif gtype == 'G_TYPE_UINT64': + self.b(' g_value_get_uint64 (args->values + %d),' % i) + elif gtype == 'G_TYPE_INT64': + self.b(' g_value_get_int64 (args->values + %d),' % i) + elif gtype == 'G_TYPE_DOUBLE': + self.b(' g_value_get_double (args->values + %d),' % i) + else: + assert False, "Don't know how to get %s from a GValue" % gtype + + self.b(' error, user_data, weak_object);') + self.b('') + + if len(out_args) > 0: + self.b(' g_value_array_free (args);') + else: + self.b(' if (args != NULL)') + self.b(' g_value_array_free (args);') + + self.b('}') + self.b('') + + # Async stub + + # Example: + # TpProxyPendingCall * + # tp_cli_properties_interface_call_get_properties + # (gpointer proxy, + # gint timeout_ms, + # const GArray *in_properties, + # tp_cli_properties_interface_callback_for_get_properties callback, + # gpointer user_data, + # GDestroyNotify *destructor); + + self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,' + % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) + self.h(' gint timeout_ms,') + + self.b('/**') + self.b(' * %s_%s_call_%s:' + % (self.prefix_lc, iface_lc, member_lc)) + self.b(' * @proxy: the #TpProxy') + self.b(' * @timeout_ms: the timeout in milliseconds, or -1 to use the') + self.b(' * default') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' * @%s: Used to pass an \'in\' argument: %s' + % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) + + self.b(' * @callback: called when the method call succeeds or fails') + self.b(' * @user_data: user-supplied data passed to the callback') + self.b(' * @destroy: called with the user_data as argument, after the') + self.b(' * call has succeeded, failed or been cancelled') + self.b(' * @weak_object: A #GObject which will be weakly referenced; ') + self.b(' * if it is destroyed, this callback will automatically be') + self.b(' * disconnected') + self.b(' *') + self.b(' * Start a %s method call.' % member) + self.b(' *') + self.b(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) + self.b(' *') + self.b(' * Returns: a #TpProxyPendingCall representing the call in') + self.b(' * progress. It is borrowed from the object, and will become') + self.b(' * invalid when the callback is called, the call is') + self.b(' * cancelled or the #TpProxy becomes invalid.') + self.b(' */') + self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,' + % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) + self.b(' gint timeout_ms,') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.h(' %s%s%s,' % (const, ctype, name)) + self.b(' %s%s%s,' % (const, ctype, name)) + + self.h(' %s callback,' % callback_name) + self.h(' gpointer user_data,') + self.h(' GDestroyNotify destroy,') + self.h(' GObject *weak_object);') + self.h('') + + self.b(' %s callback,' % callback_name) + self.b(' gpointer user_data,') + self.b(' GDestroyNotify destroy,') + self.b(' GObject *weak_object)') + self.b('{') + self.b(' GError *error = NULL;') + self.b(' GQuark interface = %s;' % self.get_iface_quark()) + self.b(' DBusGProxy *iface;') + self.b('') + self.b(' g_return_val_if_fail (%s (proxy), NULL);' + % self.proxy_assert) + self.b('') + self.b(' iface = tp_proxy_borrow_interface_by_id (') + self.b(' (TpProxy *) proxy,') + self.b(' interface, &error);') + self.b('') + self.b(' if (iface == NULL)') + self.b(' {') + self.b(' if (callback != NULL)') + self.b(' callback (proxy,') + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + if pointer: + self.b(' NULL,') + else: + self.b(' 0,') + + self.b(' error, user_data, weak_object);') + self.b(' return NULL;') + self.b(' }') + self.b('') + self.b(' if (callback == NULL)') + self.b(' {') + self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.b(' %s, %s,' % (gtype, name)) + + self.b(' G_TYPE_INVALID);') + self.b(' return NULL;') + self.b(' }') + self.b(' else') + self.b(' {') + self.b(' TpProxyPendingCall *data;') + self.b('') + self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') + self.b(' interface, "%s", iface,' % member) + self.b(' %s,' % invoke_callback) + self.b(' G_CALLBACK (callback), user_data, destroy,') + self.b(' weak_object, FALSE);') + self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') + self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') + self.b(' "%s",' % member) + self.b(' %s,' % collect_callback) + self.b(' data,') + self.b(' tp_proxy_pending_call_v0_completed,') + self.b(' timeout_ms,') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.b(' %s, %s,' % (gtype, name)) + + self.b(' G_TYPE_INVALID));') + self.b('') + self.b(' return data;') + self.b(' }') + self.b('}') + self.b('') + + # Reentrant blocking calls + # Example: + # gboolean tp_cli_properties_interface_run_get_properties + # (gpointer proxy, + # gint timeout_ms, + # const GArray *in_properties, + # GPtrArray **out0, + # GError **error, + # GMainLoop **loop); + + self.b('typedef struct {') + self.b(' GMainLoop *loop;') + self.b(' GError **error;') + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' %s*%s;' % (ctype, name)) + + self.b(' gboolean success:1;') + self.b(' gboolean completed:1;') + self.b('} _%s_%s_run_state_%s;' + % (self.prefix_lc, iface_lc, member_lc)) + + reentrant_invoke = '_%s_%s_finish_running_%s' % (self.prefix_lc, + iface_lc, + member_lc) + + self.b('static void') + self.b('%s (TpProxy *self,' % reentrant_invoke) + self.b(' GError *error,') + self.b(' GValueArray *args,') + self.b(' GCallback unused,') + self.b(' gpointer user_data,') + self.b(' GObject *unused2)') + self.b('{') + self.b(' _%s_%s_run_state_%s *state = user_data;' + % (self.prefix_lc, iface_lc, member_lc)) + self.b('') + self.b(' state->success = (error == NULL);') + self.b(' state->completed = TRUE;') + self.b(' g_main_loop_quit (state->loop);') + self.b('') + self.b(' if (error != NULL)') + self.b(' {') + self.b(' if (state->error != NULL)') + self.b(' *state->error = error;') + self.b(' else') + self.b(' g_error_free (error);') + self.b('') + self.b(' return;') + self.b(' }') + self.b('') + + for i, arg in enumerate(out_args): + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' if (state->%s != NULL)' % name) + if marshaller == 'BOXED': + self.b(' *state->%s = g_value_dup_boxed (' + 'args->values + %d);' % (name, i)) + elif marshaller == 'STRING': + self.b(' *state->%s = g_value_dup_string ' + '(args->values + %d);' % (name, i)) + elif marshaller in ('UCHAR', 'BOOLEAN', 'INT', 'UINT', + 'INT64', 'UINT64', 'DOUBLE'): + self.b(' *state->%s = g_value_get_%s (args->values + %d);' + % (name, marshaller.lower(), i)) + else: + assert False, "Don't know how to copy %s" % gtype + + self.b('') + + if len(out_args) > 0: + self.b(' g_value_array_free (args);') + else: + self.b(' if (args != NULL)') + self.b(' g_value_array_free (args);') + + self.b('}') + self.b('') + + self.h('gboolean %s_%s_run_%s (%sproxy,' + % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) + self.h(' gint timeout_ms,') + + self.b('/**') + self.b(' * %s_%s_run_%s:' % (self.prefix_lc, iface_lc, member_lc)) + self.b(' * @proxy: %s' % self.proxy_doc) + self.b(' * @timeout_ms: Timeout in milliseconds, or -1 for default') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' * @%s: Used to pass an \'in\' argument: %s' + % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.b(' * @%s: Used to return an \'out\' argument if %%TRUE is ' + 'returned: %s' + % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) + + self.b(' * @error: If not %NULL, used to return errors if %FALSE ') + self.b(' * is returned') + self.b(' * @loop: If not %NULL, set before re-entering ') + self.b(' * the main loop, to point to a #GMainLoop ') + self.b(' * which can be used to cancel this call with ') + self.b(' * g_main_loop_quit(), causing a return of ') + self.b(' * %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED') + self.b(' *') + self.b(' * Call the method %s and run the main loop' % member) + self.b(' * until it returns. Before calling this method, you must') + self.b(' * add a reference to any borrowed objects you need to keep,') + self.b(' * and generally ensure that everything is in a consistent') + self.b(' * state.') + self.b(' *') + self.b(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) + self.b(' *') + self.b(' * Returns: TRUE on success, FALSE and sets @error on error') + self.b(' */') + self.b('gboolean\n%s_%s_run_%s (%sproxy,' + % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) + self.b(' gint timeout_ms,') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.h(' %s%s%s,' % (const, ctype, name)) + self.b(' %s%s%s,' % (const, ctype, name)) + + for arg in out_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + self.h(' %s*%s,' % (ctype, name)) + self.b(' %s*%s,' % (ctype, name)) + + self.h(' GError **error,') + self.h(' GMainLoop **loop);') + self.h('') + + self.b(' GError **error,') + self.b(' GMainLoop **loop)') + self.b('{') + self.b(' DBusGProxy *iface;') + self.b(' GQuark interface = %s;' % self.get_iface_quark()) + self.b(' TpProxyPendingCall *pc;') + self.b(' _%s_%s_run_state_%s state = {' + % (self.prefix_lc, iface_lc, member_lc)) + self.b(' NULL /* loop */, error,') + + for arg in out_args: + name, info, tp_type, elt = arg + + self.b(' %s,' % name) + + self.b(' FALSE /* completed */, FALSE /* success */ };') + self.b('') + self.b(' g_return_val_if_fail (%s (proxy), FALSE);' + % self.proxy_assert) + self.b('') + self.b(' iface = tp_proxy_borrow_interface_by_id') + self.b(' ((TpProxy *) proxy, interface, error);') + self.b('') + self.b(' if (iface == NULL)') + self.b(' return FALSE;') + self.b('') + self.b(' state.loop = g_main_loop_new (NULL, FALSE);') + self.b('') + self.b(' pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') + self.b(' interface, "%s", iface,' % member) + self.b(' %s,' % reentrant_invoke) + self.b(' NULL, &state, NULL, NULL, TRUE);') + self.b('') + self.b(' if (loop != NULL)') + self.b(' *loop = state.loop;') + self.b('') + self.b(' tp_proxy_pending_call_v0_take_pending_call (pc,') + self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') + self.b(' "%s",' % member) + self.b(' %s,' % collect_callback) + self.b(' pc,') + self.b(' tp_proxy_pending_call_v0_completed,') + self.b(' timeout_ms,') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + + const = pointer and 'const ' or '' + + self.b(' %s, %s,' % (gtype, name)) + + self.b(' G_TYPE_INVALID));') + self.b('') + self.b(' if (!state.completed)') + self.b(' g_main_loop_run (state.loop);') + self.b('') + self.b(' if (!state.completed)') + self.b(' tp_proxy_pending_call_cancel (pc);') + self.b('') + self.b(' if (loop != NULL)') + self.b(' *loop = NULL;') + self.b('') + self.b(' g_main_loop_unref (state.loop);') + self.b('') + self.b(' return state.success;') + self.b('}') + self.b('') + + # leave a gap for the end of the method + self.b('') + self.h('') + + def do_signal_add(self, signal): + marshaller_items = [] + gtypes = [] + + for i in signal.getElementsByTagName('arg'): + name = i.getAttribute('name') + type = i.getAttribute('type') + info = type_to_gtype(type) + # type, GType, STRING, is a pointer + gtypes.append(info[1]) + + self.b(' dbus_g_proxy_add_signal (proxy, "%s",' + % signal.getAttribute('name')) + for gtype in gtypes: + self.b(' %s,' % gtype) + self.b(' G_TYPE_INVALID);') + + def do_interface(self, node): + ifaces = node.getElementsByTagName('interface') + assert len(ifaces) == 1 + iface = ifaces[0] + name = node.getAttribute('name').replace('/', '') + + self.iface = name + self.iface_lc = name.lower() + self.iface_uc = name.upper() + self.iface_mc = name.replace('_', '') + self.iface_dbus = iface.getAttribute('name') + + signals = node.getElementsByTagName('signal') + methods = node.getElementsByTagName('method') + + self.b('static inline void') + self.b('%s_add_signals_for_%s (DBusGProxy *proxy)' + % (self.prefix_lc, name.lower())) + self.b('{') + + for signal in signals: + self.do_signal_add(signal) + + self.b('}') + self.b('') + self.b('') + + for signal in signals: + self.do_signal(name, signal) + + for method in methods: + self.do_method(name, method) + + self.iface_dbus = None + + def __call__(self): + + self.h('G_BEGIN_DECLS') + self.h('') + + self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get') + self.b(' * confused by seeing function definitions, so mark it as: */') + self.b('/**/') + self.b('') + + nodes = self.dom.getElementsByTagName('node') + nodes.sort(cmp_by_name) + + for node in nodes: + self.do_interface(node) + + if self.group is not None: + + self.b('/*') + self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group)) + self.b(' * @self: the #TpProxy') + self.b(' * @quark: a quark whose string value is the interface') + self.b(' * name whose signals should be added') + self.b(' * @proxy: the D-Bus proxy to which to add the signals') + self.b(' * @unused: not used for anything') + self.b(' *') + self.b(' * Tell dbus-glib that @proxy has the signatures of all') + self.b(' * signals on the given interface, if it\'s one we') + self.b(' * support.') + self.b(' *') + self.b(' * This function should be used as a signal handler for') + self.b(' * #TpProxy::interface-added.') + self.b(' */') + self.b('static void') + self.b('%s_%s_add_signals (TpProxy *self,' + % (self.prefix_lc, self.group)) + self.b(' guint quark,') + self.b(' DBusGProxy *proxy,') + self.b(' gpointer unused)') + + self.b('{') + + for node in nodes: + iface = node.getElementsByTagName('interface')[0] + self.iface_dbus = iface.getAttribute('name') + name = node.getAttribute('name').replace('/', '').lower() + self.iface_uc = name.upper() + self.b(' if (quark == %s)' % self.get_iface_quark()) + self.b(' %s_add_signals_for_%s (proxy);' + % (self.prefix_lc, name)) + + self.b('}') + self.b('') + + self.h('G_END_DECLS') + self.h('') + + open(self.basename + '.h', 'w').write('\n'.join(self.__header)) + open(self.basename + '-body.h', 'w').write('\n'.join(self.__body)) + + +def types_to_gtypes(types): + return [type_to_gtype(t)[1] for t in types] + + +if __name__ == '__main__': + options, argv = gnu_getopt(sys.argv[1:], '', + ['group=', 'subclass=', 'subclass-assert=', + 'iface-quark-prefix=']) + + opts = {} + + for option, value in options: + opts[option] = value + + dom = xml.dom.minidom.parse(argv[0]) + + Generator(dom, argv[1], argv[2], opts)() diff --git a/tools/glib-client-marshaller-gen.py b/tools/glib-client-marshaller-gen.py new file mode 100644 index 00000000..54447255 --- /dev/null +++ b/tools/glib-client-marshaller-gen.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import sys +import xml.dom.minidom +from string import ascii_letters, digits + + +from libglibcodegen import signal_to_marshal_name + + +NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + +class Generator(object): + + def __init__(self, dom, prefix): + self.dom = dom + self.marshallers = {} + self.prefix = prefix + + def do_signal(self, signal): + marshaller = signal_to_marshal_name(signal, self.prefix) + + assert '__' in marshaller + rhs = marshaller.split('__', 1)[1].split('_') + + self.marshallers[marshaller] = rhs + + def __call__(self): + signals = self.dom.getElementsByTagName('signal') + + for signal in signals: + self.do_signal(signal) + + print 'void' + print '%s_register_dbus_glib_marshallers (void)' % self.prefix + print '{' + + all = self.marshallers.keys() + all.sort() + for marshaller in all: + rhs = self.marshallers[marshaller] + + print ' dbus_g_object_register_marshaller (%s,' % marshaller + print ' G_TYPE_NONE, /* return */' + for type in rhs: + print ' G_TYPE_%s,' % type.replace('VOID', 'NONE') + print ' G_TYPE_INVALID);' + + print '}' + + +def types_to_gtypes(types): + return [type_to_gtype(t)[1] for t in types] + +if __name__ == '__main__': + argv = sys.argv[1:] + dom = xml.dom.minidom.parse(argv[0]) + + Generator(dom, argv[1])() diff --git a/tools/glib-errors-enum-body.xsl b/tools/glib-errors-enum-body.xsl new file mode 100644 index 00000000..17054b76 --- /dev/null +++ b/tools/glib-errors-enum-body.xsl @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + /* + */ + { TP_ERROR_, "TP_ERROR_", "" }, + + + + + /* Generated from the Telepathy spec + + + + + +*/ + +#include <_gen/telepathy-errors.h> + +GType +tp_error_get_type (void) +{ + static GType etype = 0; + if (G_UNLIKELY (etype == 0)) + { + static const GEnumValue values[] = { + }; + + etype = g_enum_register_static ("TpError", values); + } + return etype; +} + + + + + + diff --git a/tools/glib-errors-enum-header.xsl b/tools/glib-errors-enum-header.xsl new file mode 100644 index 00000000..5275041c --- /dev/null +++ b/tools/glib-errors-enum-header.xsl @@ -0,0 +1,73 @@ + + + + + + + + * @TP_ERROR_: : + * + + + + TP_ERROR_, + + + + + /* Generated from the Telepathy spec + + + + + +*/ + +#include <glib-object.h> + +G_BEGIN_DECLS + +GType tp_error_get_type (void); + +/** + * TP_TYPE_ERROR: + * + * The GType of the Telepathy error enumeration. + */ +#define TP_TYPE_ERROR (tp_error_get_type()) + +/** + * TpError: + * + * Enumerated type representing the Telepathy D-Bus errors. + */ +typedef enum { +} TpError; + +G_END_DECLS + + + + + diff --git a/tools/glib-ginterface-gen.py b/tools/glib-ginterface-gen.py new file mode 100644 index 00000000..0c7c419e --- /dev/null +++ b/tools/glib-ginterface-gen.py @@ -0,0 +1,711 @@ +#!/usr/bin/python + +# glib-ginterface-gen.py: service-side interface generator +# +# Generate dbus-glib 0.x service GInterfaces from the Telepathy specification. +# The master copy of this program is in the telepathy-glib repository - +# please make any changes there. +# +# Copyright (C) 2006, 2007 Collabora Limited +# +# 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import sys +import os.path +import xml.dom.minidom + +from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \ + camelcase_to_lower, NS_TP, dbus_gutils_wincaps_to_uscore, \ + signal_to_marshal_name, method_to_glue_marshal_name + + +NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + +class Generator(object): + + def __init__(self, dom, prefix, basename, signal_marshal_prefix, + headers, end_headers, not_implemented_func, + allow_havoc): + self.dom = dom + self.__header = [] + self.__body = [] + + assert prefix.endswith('_') + assert not signal_marshal_prefix.endswith('_') + + # The main_prefix, sub_prefix thing is to get: + # FOO_ -> (FOO_, _) + # FOO_SVC_ -> (FOO_, _SVC_) + # but + # FOO_BAR/ -> (FOO_BAR_, _) + # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_) + + if '/' in prefix: + main_prefix, sub_prefix = prefix.upper().split('/', 1) + prefix = prefix.replace('/', '_') + else: + main_prefix, sub_prefix = prefix.upper().split('_', 1) + + self.MAIN_PREFIX_ = main_prefix + '_' + self._SUB_PREFIX_ = '_' + sub_prefix + + self.Prefix_ = prefix + self.Prefix = prefix.replace('_', '') + self.prefix_ = prefix.lower() + self.PREFIX_ = prefix.upper() + + self.signal_marshal_prefix = signal_marshal_prefix + self.headers = headers + self.end_headers = end_headers + self.not_implemented_func = not_implemented_func + self.allow_havoc = allow_havoc + + def h(self, s): + self.__header.append(s) + + def b(self, s): + self.__body.append(s) + + def do_node(self, node): + node_name = node.getAttribute('name').replace('/', '') + node_name_mixed = self.node_name_mixed = node_name.replace('_', '') + node_name_lc = self.node_name_lc = node_name.lower() + node_name_uc = self.node_name_uc = node_name.upper() + + interfaces = node.getElementsByTagName('interface') + assert len(interfaces) == 1, interfaces + interface = interfaces[0] + self.iface_name = interface.getAttribute('name') + + tmp = interface.getAttribute('tp:implement-service') + if tmp == "no": + return + + tmp = interface.getAttribute('tp:causes-havoc') + if tmp and not self.allow_havoc: + raise AssertionError('%s is %s' % (self.iface_name, tmp)) + + self.b('static const DBusGObjectInfo _%s%s_object_info;' + % (self.prefix_, node_name_lc)) + self.b('') + + methods = interface.getElementsByTagName('method') + signals = interface.getElementsByTagName('signal') + properties = interface.getElementsByTagName('property') + # Don't put properties in dbus-glib glue + glue_properties = [] + + self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed)) + self.b(' GTypeInterface parent_class;') + for method in methods: + self.b(' %s %s;' % self.get_method_impl_names(method)) + self.b('};') + self.b('') + + if signals: + self.b('enum {') + for signal in signals: + self.b(' %s,' % self.get_signal_const_entry(signal)) + self.b(' N_%s_SIGNALS' % node_name_uc) + self.b('};') + self.b('static guint %s_signals[N_%s_SIGNALS] = {0};' + % (node_name_lc, node_name_uc)) + self.b('') + + self.b('static void %s%s_base_init (gpointer klass);' + % (self.prefix_, node_name_lc)) + self.b('') + + self.b('GType') + self.b('%s%s_get_type (void)' + % (self.prefix_, node_name_lc)) + self.b('{') + self.b(' static GType type = 0;') + self.b('') + self.b(' if (G_UNLIKELY (type == 0))') + self.b(' {') + self.b(' static const GTypeInfo info = {') + self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed)) + self.b(' %s%s_base_init, /* base_init */' + % (self.prefix_, node_name_lc)) + self.b(' NULL, /* base_finalize */') + self.b(' NULL, /* class_init */') + self.b(' NULL, /* class_finalize */') + self.b(' NULL, /* class_data */') + self.b(' 0,') + self.b(' 0, /* n_preallocs */') + self.b(' NULL /* instance_init */') + self.b(' };') + self.b('') + self.b(' type = g_type_register_static (G_TYPE_INTERFACE,') + self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed)) + self.b(' }') + self.b('') + self.b(' return type;') + self.b('}') + self.b('') + + self.h('/**') + self.h(' * %s%s:' % (self.Prefix, node_name_mixed)) + self.h(' *') + self.h(' * Dummy typedef representing any implementation of this ' + 'interface.') + self.h(' */') + self.h('typedef struct _%s%s %s%s;' + % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) + self.h('') + self.h('/**') + self.h(' * %s%sClass:' % (self.Prefix, node_name_mixed)) + self.h(' *') + self.h(' * The class of %s%s.' % (self.Prefix, node_name_mixed)) + self.h(' */') + self.h('typedef struct _%s%sClass %s%sClass;' + % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed)) + self.h('') + self.h('GType %s%s_get_type (void);' + % (self.prefix_, node_name_lc)) + + gtype = self.current_gtype = \ + self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc + classname = self.Prefix + node_name_mixed + + self.h('#define %s \\\n (%s%s_get_type ())' + % (gtype, self.prefix_, node_name_lc)) + self.h('#define %s%s(obj) \\\n' + ' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))' + % (self.PREFIX_, node_name_uc, gtype, classname)) + self.h('#define %sIS%s%s(obj) \\\n' + ' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))' + % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype)) + self.h('#define %s%s_GET_CLASS(obj) \\\n' + ' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))' + % (self.PREFIX_, node_name_uc, gtype, classname)) + self.h('') + self.h('') + + base_init_code = [] + + for method in methods: + self.do_method(method) + + for signal in signals: + base_init_code.extend(self.do_signal(signal)) + + self.b('static inline void') + self.b('%s%s_base_init_once (gpointer klass)' + % (self.prefix_, node_name_lc)) + self.b('{') + self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {' + % (len(properties) + 1)) + + for m in properties: + access = m.getAttribute('access') + assert access in ('read', 'write', 'readwrite') + + if access == 'read': + flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ' + elif access == 'write': + flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE' + else: + flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | ' + 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE') + + self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */' + % (flags, m.getAttribute('type'), m.getAttribute('name'))) + + self.b(' { 0, 0, NULL, 0, NULL, NULL }') + self.b(' };') + self.b(' static TpDBusPropertiesMixinIfaceInfo interface =') + self.b(' { 0, properties, NULL, NULL };') + self.b('') + self.b(' interface.dbus_interface = g_quark_from_static_string ' + '("%s");' % self.iface_name) + + for i, m in enumerate(properties): + self.b(' properties[%d].name = g_quark_from_static_string ("%s");' + % (i, m.getAttribute('name'))) + self.b(' properties[%d].type = %s;' + % (i, type_to_gtype(m.getAttribute('type'))[1])) + + self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);' + % self.current_gtype) + + self.b('') + for s in base_init_code: + self.b(s) + self.b(' dbus_g_object_type_install_info (%s%s_get_type (),' + % (self.prefix_, node_name_lc)) + self.b(' &_%s%s_object_info);' + % (self.prefix_, node_name_lc)) + self.b('}') + + self.b('static void') + self.b('%s%s_base_init (gpointer klass)' + % (self.prefix_, node_name_lc)) + self.b('{') + self.b(' static gboolean initialized = FALSE;') + self.b('') + self.b(' if (!initialized)') + self.b(' {') + self.b(' initialized = TRUE;') + self.b(' %s%s_base_init_once (klass);' + % (self.prefix_, node_name_lc)) + self.b(' }') + # insert anything we need to do per implementation here + self.b('}') + + self.h('') + + self.b('static const DBusGMethodInfo _%s%s_methods[] = {' + % (self.prefix_, node_name_lc)) + + method_blob, offsets = self.get_method_glue(methods) + + for method, offset in zip(methods, offsets): + self.do_method_glue(method, offset) + + self.b('};') + self.b('') + + self.b('static const DBusGObjectInfo _%s%s_object_info = {' + % (self.prefix_, node_name_lc)) + self.b(' 0,') # version + self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc)) + self.b(' %d,' % len(methods)) + self.b('"' + method_blob.replace('\0', '\\0') + '",') + self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",') + self.b('"' + + self.get_property_glue(glue_properties).replace('\0', '\\0') + + '",') + self.b('};') + self.b('') + + self.node_name_mixed = None + self.node_name_lc = None + self.node_name_uc = None + + def get_method_glue(self, methods): + info = [] + offsets = [] + + for method in methods: + offsets.append(len(''.join(info))) + + info.append(self.iface_name + '\0') + info.append(method.getAttribute('name') + '\0') + + info.append('A\0') # async + + counter = 0 + for arg in method.getElementsByTagName('arg'): + out = arg.getAttribute('direction') == 'out' + + name = arg.getAttribute('name') + if not name: + assert out + name = 'arg%u' % counter + counter += 1 + + info.append(name + '\0') + + if out: + info.append('O\0') + else: + info.append('I\0') + + if out: + info.append('F\0') # not const + info.append('N\0') # not error or return + info.append(arg.getAttribute('type') + '\0') + + info.append('\0') + + return ''.join(info) + '\0', offsets + + def do_method_glue(self, method, offset): + lc_name = camelcase_to_lower(method.getAttribute('name')) + + marshaller = method_to_glue_marshal_name(method, + self.signal_marshal_prefix) + wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name + + self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset)) + + def get_signal_glue(self, signals): + info = [] + + for signal in signals: + info.append(self.iface_name) + info.append(signal.getAttribute('name')) + + return '\0'.join(info) + '\0\0' + + # the implementation can be the same + get_property_glue = get_signal_glue + + def get_method_impl_names(self, method): + dbus_method_name = method.getAttribute('name') + class_member_name = camelcase_to_lower(dbus_method_name) + stub_name = (self.prefix_ + self.node_name_lc + '_' + + class_member_name) + return (stub_name + '_impl', class_member_name) + + def do_method(self, method): + assert self.node_name_mixed is not None + + in_class = [] + + # Examples refer to Thing.DoStuff (su) -> ii + + # DoStuff + dbus_method_name = method.getAttribute('name') + # do_stuff + class_member_name = camelcase_to_lower(dbus_method_name) + # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint, + # DBusGMethodInvocation *); + stub_name = (self.prefix_ + self.node_name_lc + '_' + + class_member_name) + # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *, + # const char *, guint, DBusGMethodInvocation); + impl_name = stub_name + '_impl' + # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *, + # gint, gint); + ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' + + class_member_name) + + # Gather arguments + in_args = [] + out_args = [] + for i in method.getElementsByTagName('arg'): + name = i.getAttribute('name') + direction = i.getAttribute('direction') or 'in' + dtype = i.getAttribute('type') + + assert direction in ('in', 'out') + + if name: + name = direction + '_' + name + elif direction == 'in': + name = direction + str(len(in_args)) + else: + name = direction + str(len(out_args)) + + ctype, gtype, marshaller, pointer = type_to_gtype(dtype) + + if pointer: + ctype = 'const ' + ctype + + struct = (ctype, name) + + if direction == 'in': + in_args.append(struct) + else: + out_args.append(struct) + + # Implementation type declaration (in header, docs in body) + self.b('/**') + self.b(' * %s:' % impl_name) + self.b(' * @self: The object implementing this interface') + for (ctype, name) in in_args: + self.b(' * @%s: %s (FIXME, generate documentation)' + % (name, ctype)) + self.b(' * @context: Used to return values or throw an error') + self.b(' *') + self.b(' * The signature of an implementation of the D-Bus method') + self.b(' * %s on interface %s.' % (dbus_method_name, self.iface_name)) + self.b(' */') + self.h('typedef void (*%s) (%s%s *self,' + % (impl_name, self.Prefix, self.node_name_mixed)) + for (ctype, name) in in_args: + self.h(' %s%s,' % (ctype, name)) + self.h(' DBusGMethodInvocation *context);') + + # Class member (in class definition) + in_class.append(' %s %s;' % (impl_name, class_member_name)) + + # Stub definition (in body only - it's static) + self.b('static void') + self.b('%s (%s%s *self,' + % (stub_name, self.Prefix, self.node_name_mixed)) + for (ctype, name) in in_args: + self.b(' %s%s,' % (ctype, name)) + self.b(' DBusGMethodInvocation *context)') + self.b('{') + self.b(' %s impl = (%s%s_GET_CLASS (self)->%s);' + % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name)) + self.b('') + self.b(' if (impl != NULL)') + tmp = ['self'] + [name for (ctype, name) in in_args] + ['context'] + self.b(' {') + self.b(' (impl) (%s);' % ',\n '.join(tmp)) + self.b(' }') + self.b(' else') + self.b(' {') + if self.not_implemented_func: + self.b(' %s (context);' % self.not_implemented_func) + else: + self.b(' GError e = { DBUS_GERROR, ') + self.b(' DBUS_GERROR_UNKNOWN_METHOD,') + self.b(' "Method not implemented" };') + self.b('') + self.b(' dbus_g_method_return_error (context, &e);') + self.b(' }') + self.b('}') + self.b('') + + # Implementation registration (in both header and body) + self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);' + % (self.prefix_, self.node_name_lc, class_member_name, + self.Prefix, self.node_name_mixed, impl_name)) + + self.b('/**') + self.b(' * %s%s_implement_%s:' + % (self.prefix_, self.node_name_lc, class_member_name)) + self.b(' * @klass: A class whose instances implement this interface') + self.b(' * @impl: A callback used to implement the %s D-Bus method' + % dbus_method_name) + self.b(' *') + self.b(' * Register an implementation for the %s method in the vtable' + % dbus_method_name) + self.b(' * of an implementation of this interface. To be called from') + self.b(' * the interface init function.') + self.b(' */') + self.b('void') + self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)' + % (self.prefix_, self.node_name_lc, class_member_name, + self.Prefix, self.node_name_mixed, impl_name)) + self.b('{') + self.b(' klass->%s = impl;' % class_member_name) + self.b('}') + self.b('') + + # Return convenience function (static inline, in header) + self.h('/**') + self.h(' * %s:' % ret_name) + self.h(' * @context: The D-Bus method invocation context') + for (ctype, name) in out_args: + self.h(' * @%s: %s (FIXME, generate documentation)' + % (name, ctype)) + self.h(' *') + self.h(' * Return successfully by calling dbus_g_method_return().') + self.h(' * This inline function exists only to provide type-safety.') + self.h(' */') + tmp = (['DBusGMethodInvocation *context'] + + [ctype + name for (ctype, name) in out_args]) + self.h('static inline') + self.h('/* this comment is to stop gtkdoc realising this is static */') + self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');') + self.h('static inline void') + self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')') + self.h('{') + tmp = ['context'] + [name for (ctype, name) in out_args] + self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');') + self.h('}') + self.h('') + + return in_class + + def get_signal_const_entry(self, signal): + assert self.node_name_uc is not None + return ('SIGNAL_%s_%s' + % (self.node_name_uc, signal.getAttribute('name'))) + + def do_signal(self, signal): + assert self.node_name_mixed is not None + + in_base_init = [] + + # for signal: Thing::StuffHappened (s, u) + # we want to emit: + # void tp_svc_thing_emit_stuff_happened (gpointer instance, + # const char *arg0, guint arg1); + + dbus_name = signal.getAttribute('name') + stub_name = (self.prefix_ + self.node_name_lc + '_emit_' + + camelcase_to_lower(dbus_name)) + const_name = self.get_signal_const_entry(signal) + + # Gather arguments + args = [] + for i in signal.getElementsByTagName('arg'): + name = i.getAttribute('name') + dtype = i.getAttribute('type') + tp_type = i.getAttribute('tp:type') + + if name: + name = 'arg_' + name + else: + name = 'arg' + str(len(args)) + + ctype, gtype, marshaller, pointer = type_to_gtype(dtype) + + if pointer: + ctype = 'const ' + ctype + + struct = (ctype, name, gtype) + args.append(struct) + + tmp = (['gpointer instance'] + + [ctype + name for (ctype, name, gtype) in args]) + + self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');') + + # FIXME: emit docs + + self.b('/**') + self.b(' * %s:' % stub_name) + self.b(' * @instance: The object implementing this interface') + for (ctype, name, gtype) in args: + self.b(' * @%s: %s (FIXME, generate documentation)' + % (name, ctype)) + self.b(' *') + self.b(' * Type-safe wrapper around g_signal_emit to emit the') + self.b(' * %s signal on interface %s.' + % (dbus_name, self.iface_name)) + self.b(' */') + + self.b('void') + self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')') + self.b('{') + self.b(' g_assert (instance != NULL);') + self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));' + % (self.current_gtype)) + tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name), + '0'] + [name for (ctype, name, gtype) in args]) + self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');') + self.b('}') + self.b('') + + in_base_init.append(' %s_signals[%s] =' + % (self.node_name_lc, const_name)) + in_base_init.append(' g_signal_new ("%s",' + % (dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-'))) + in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),') + in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,') + in_base_init.append(' 0,') + in_base_init.append(' NULL, NULL,') + in_base_init.append(' %s,' + % signal_to_marshal_name(signal, self.signal_marshal_prefix)) + in_base_init.append(' G_TYPE_NONE,') + tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args] + in_base_init.append(' %s);' % ',\n '.join(tmp)) + in_base_init.append('') + + return in_base_init + + def __call__(self): + self.h('#include ') + self.h('#include ') + self.h('#include ') + self.h('') + self.h('G_BEGIN_DECLS') + self.h('') + + self.b('#include "%s.h"' % basename) + self.b('') + for header in self.headers: + self.b('#include %s' % header) + self.b('') + + nodes = self.dom.getElementsByTagName('node') + nodes.sort(cmp_by_name) + + for node in nodes: + self.do_node(node) + + self.h('') + self.h('G_END_DECLS') + + self.b('') + for header in self.end_headers: + self.b('#include %s' % header) + + self.h('') + self.b('') + open(basename + '.h', 'w').write('\n'.join(self.__header)) + open(basename + '.c', 'w').write('\n'.join(self.__body)) + + +def cmdline_error(): + print """\ +usage: + gen-ginterface [OPTIONS] xmlfile Prefix_ +options: + --include='' (may be repeated) + --include='"header.h"' (ditto) + --include-end='"header.h"' (ditto) + Include extra headers in the generated .c file + --signal-marshal-prefix='prefix' + Use the given prefix on generated signal marshallers (default is + prefix.lower()). + --filename='BASENAME' + Set the basename for the output files (default is prefix.lower() + + 'ginterfaces') + --not-implemented-func='symbol' + Set action when methods not implemented in the interface vtable are + called. symbol must have signature + void symbol (DBusGMethodInvocation *context) + and return some sort of "not implemented" error via + dbus_g_method_return_error (context, ...) +""" + sys.exit(1) + + +if __name__ == '__main__': + from getopt import gnu_getopt + + options, argv = gnu_getopt(sys.argv[1:], '', + ['filename=', 'signal-marshal-prefix=', + 'include=', 'include-end=', + 'allow-unstable', + 'not-implemented-func=']) + + try: + prefix = argv[1] + except IndexError: + cmdline_error() + + basename = prefix.lower() + 'ginterfaces' + signal_marshal_prefix = prefix.lower().rstrip('_') + headers = [] + end_headers = [] + not_implemented_func = '' + allow_havoc = False + + for option, value in options: + if option == '--filename': + basename = value + elif option == '--signal-marshal-prefix': + signal_marshal_prefix = value + elif option == '--include': + if value[0] not in '<"': + value = '"%s"' % value + headers.append(value) + elif option == '--include-end': + if value[0] not in '<"': + value = '"%s"' % value + end_headers.append(value) + elif option == '--not-implemented-func': + not_implemented_func = value + elif option == '--allow-unstable': + allow_havoc = True + + try: + dom = xml.dom.minidom.parse(argv[0]) + except IndexError: + cmdline_error() + + Generator(dom, prefix, basename, signal_marshal_prefix, headers, + end_headers, not_implemented_func, allow_havoc)() diff --git a/tools/glib-gtypes-generator.py b/tools/glib-gtypes-generator.py new file mode 100644 index 00000000..fcb46e84 --- /dev/null +++ b/tools/glib-gtypes-generator.py @@ -0,0 +1,230 @@ +#!/usr/bin/python + +# Generate GLib GInterfaces from the Telepathy specification. +# The master copy of this program is in the telepathy-glib repository - +# please make any changes there. +# +# Copyright (C) 2006, 2007 Collabora Limited +# +# 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import sys +import xml.dom.minidom + +from libglibcodegen import escape_as_identifier, \ + get_docstring, \ + NS_TP, \ + Signature, \ + type_to_gtype, \ + xml_escape + + +def types_to_gtypes(types): + return [type_to_gtype(t)[1] for t in types] + + +class GTypesGenerator(object): + def __init__(self, dom, output, mixed_case_prefix): + self.dom = dom + self.Prefix = mixed_case_prefix + self.PREFIX_ = self.Prefix.upper() + '_' + self.prefix_ = self.Prefix.lower() + '_' + + self.header = open(output + '.h', 'w') + self.body = open(output + '-body.h', 'w') + + for f in (self.header, self.body): + f.write('/* Auto-generated, do not edit.\n *\n' + ' * This file may be distributed under the same terms\n' + ' * as the specification from which it was generated.\n' + ' */\n\n') + + self.need_mappings = {} + self.need_structs = {} + self.need_arrays = {} + + def do_mapping_header(self, mapping): + members = mapping.getElementsByTagNameNS(NS_TP, 'member') + assert len(members) == 2 + + impl_sig = ''.join([elt.getAttribute('type') + for elt in members]) + + esc_impl_sig = escape_as_identifier(impl_sig) + + name = (self.PREFIX_ + 'HASH_TYPE_' + + mapping.getAttribute('name').upper()) + impl = self.prefix_ + 'type_dbus_hash_' + esc_impl_sig + + docstring = get_docstring(mapping) or '(Undocumented)' + + self.header.write('/**\n * %s:\n *\n' % name) + self.header.write(' * %s\n' % xml_escape(docstring)) + self.header.write(' *\n') + self.header.write(' * This macro expands to a call to a function\n') + self.header.write(' * that returns the #GType of a #GHashTable\n') + self.header.write(' * appropriate for representing a D-Bus\n') + self.header.write(' * dictionary of signature\n') + self.header.write(' * a{%s}.\n' % impl_sig) + self.header.write(' *\n') + + key, value = members + + self.header.write(' * Keys (D-Bus type %s,\n' + % key.getAttribute('type')) + tp_type = key.getAttributeNS(NS_TP, 'type') + if tp_type: + self.header.write(' * type %s,\n' % tp_type) + self.header.write(' * named %s):\n' + % key.getAttribute('name')) + docstring = get_docstring(key) or '(Undocumented)' + self.header.write(' * %s\n' % xml_escape(docstring)) + self.header.write(' *\n') + + self.header.write(' * Values (D-Bus type %s,\n' + % value.getAttribute('type')) + tp_type = value.getAttributeNS(NS_TP, 'type') + if tp_type: + self.header.write(' * type %s,\n' % tp_type) + self.header.write(' * named %s):\n' + % value.getAttribute('name')) + docstring = get_docstring(value) or '(Undocumented)' + self.header.write(' * %s\n' % xml_escape(docstring)) + self.header.write(' *\n') + + self.header.write(' */\n') + + self.header.write('#define %s (%s ())\n\n' % (name, impl)) + self.need_mappings[impl_sig] = esc_impl_sig + + def do_struct_header(self, struct): + members = struct.getElementsByTagNameNS(NS_TP, 'member') + impl_sig = ''.join([elt.getAttribute('type') for elt in members]) + esc_impl_sig = escape_as_identifier(impl_sig) + + name = (self.PREFIX_ + 'STRUCT_TYPE_' + + struct.getAttribute('name').upper()) + impl = self.prefix_ + 'type_dbus_struct_' + esc_impl_sig + docstring = struct.getElementsByTagNameNS(NS_TP, 'docstring') + if docstring: + docstring = docstring[0].toprettyxml() + if docstring.startswith(''): + docstring = docstring[14:] + if docstring.endswith('\n'): + docstring = docstring[:-16] + if docstring.strip() in ('', ''): + docstring = '(Undocumented)' + else: + docstring = '(Undocumented)' + self.header.write('/**\n * %s:\n\n' % name) + self.header.write(' * %s\n' % xml_escape(docstring)) + self.header.write(' *\n') + self.header.write(' * This macro expands to a call to a function\n') + self.header.write(' * that returns the #GType of a #GValueArray\n') + self.header.write(' * appropriate for representing a D-Bus struct\n') + self.header.write(' * with signature (%s).\n' + % impl_sig) + self.header.write(' *\n') + + for i, member in enumerate(members): + self.header.write(' * Member %d (D-Bus type ' + '%s,\n' + % (i, member.getAttribute('type'))) + tp_type = member.getAttributeNS(NS_TP, 'type') + if tp_type: + self.header.write(' * type %s,\n' % tp_type) + self.header.write(' * named %s):\n' + % member.getAttribute('name')) + docstring = get_docstring(member) or '(Undocumented)' + self.header.write(' * %s\n' % xml_escape(docstring)) + self.header.write(' *\n') + + self.header.write(' */\n') + self.header.write('#define %s (%s ())\n\n' % (name, impl)) + + array_name = struct.getAttribute('array-name') + if array_name != '': + array_name = (self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper()) + impl = self.prefix_ + 'type_dbus_array_' + esc_impl_sig + self.header.write('/**\n * %s:\n\n' % array_name) + self.header.write(' * Expands to a call to a function\n') + self.header.write(' * that returns the #GType of a #GPtrArray\n') + self.header.write(' * of #%s.\n' % name) + self.header.write(' */\n') + self.header.write('#define %s (%s ())\n\n' % (array_name, impl)) + self.need_arrays[impl_sig] = esc_impl_sig + + self.need_structs[impl_sig] = esc_impl_sig + + def __call__(self): + mappings = self.dom.getElementsByTagNameNS(NS_TP, 'mapping') + structs = self.dom.getElementsByTagNameNS(NS_TP, 'struct') + + for mapping in mappings: + self.do_mapping_header(mapping) + + for sig in self.need_mappings: + self.header.write('GType %stype_dbus_hash_%s (void);\n\n' % + (self.prefix_, self.need_mappings[sig])) + self.body.write('GType\n%stype_dbus_hash_%s (void)\n{\n' % + (self.prefix_, self.need_mappings[sig])) + self.body.write(' static GType t = 0;\n\n') + self.body.write(' if (G_UNLIKELY (t == 0))\n') + # FIXME: translate sig into two GTypes + items = tuple(Signature(sig)) + gtypes = types_to_gtypes(items) + self.body.write(' t = dbus_g_type_get_map ("GHashTable", ' + '%s, %s);\n' % (gtypes[0], gtypes[1])) + self.body.write(' return t;\n') + self.body.write('}\n\n') + + for struct in structs: + self.do_struct_header(struct) + + for sig in self.need_structs: + self.header.write('GType %stype_dbus_struct_%s (void);\n\n' % + (self.prefix_, self.need_structs[sig])) + self.body.write('GType\n%stype_dbus_struct_%s (void)\n{\n' % + (self.prefix_, self.need_structs[sig])) + self.body.write(' static GType t = 0;\n\n') + self.body.write(' if (G_UNLIKELY (t == 0))\n') + self.body.write(' t = dbus_g_type_get_struct ("GValueArray",\n') + items = tuple(Signature(sig)) + gtypes = types_to_gtypes(items) + for gtype in gtypes: + self.body.write(' %s,\n' % gtype) + self.body.write(' G_TYPE_INVALID);\n') + self.body.write(' return t;\n') + self.body.write('}\n\n') + + for sig in self.need_arrays: + self.header.write('GType %stype_dbus_array_%s (void);\n\n' % + (self.prefix_, self.need_structs[sig])) + self.body.write('GType\n%stype_dbus_array_%s (void)\n{\n' % + (self.prefix_, self.need_structs[sig])) + self.body.write(' static GType t = 0;\n\n') + self.body.write(' if (G_UNLIKELY (t == 0))\n') + self.body.write(' t = dbus_g_type_get_collection ("GPtrArray", ' + '%stype_dbus_struct_%s ());\n' % + (self.prefix_, self.need_structs[sig])) + self.body.write(' return t;\n') + self.body.write('}\n\n') + +if __name__ == '__main__': + argv = sys.argv[1:] + + dom = xml.dom.minidom.parse(argv[0]) + + GTypesGenerator(dom, argv[1], argv[2])() diff --git a/tools/glib-interfaces-body-generator.xsl b/tools/glib-interfaces-body-generator.xsl new file mode 100644 index 00000000..caff8917 --- /dev/null +++ b/tools/glib-interfaces-body-generator.xsl @@ -0,0 +1,47 @@ + + + + + + + + GQuark + + _iface_quark_ + + (void) { + static GQuark quark = 0; + if (G_UNLIKELY (quark == 0)) + { + quark = g_quark_from_static_string (" + + "); + } + return quark; + } + + + + + diff --git a/tools/glib-interfaces-generator.xsl b/tools/glib-interfaces-generator.xsl new file mode 100644 index 00000000..e703c407 --- /dev/null +++ b/tools/glib-interfaces-generator.xsl @@ -0,0 +1,55 @@ + + + + + + + + + + /** * + + _IFACE_QUARK_ + + : * * Expands to a call to a function that + returns a quark for the interface name " + + " */ #define + + _IFACE_QUARK_ + + \ ( + + _iface_quark_ + + ()) GQuark + + _iface_quark_ + + (void); + + + + + diff --git a/tools/glib-signals-marshal-gen.py b/tools/glib-signals-marshal-gen.py new file mode 100644 index 00000000..0d02c134 --- /dev/null +++ b/tools/glib-signals-marshal-gen.py @@ -0,0 +1,55 @@ +#!/usr/bin/python + +import sys +import xml.dom.minidom +from string import ascii_letters, digits + + +from libglibcodegen import signal_to_marshal_name, method_to_glue_marshal_name + + +class Generator(object): + + def __init__(self, dom): + self.dom = dom + self.marshallers = {} + + def do_method(self, method): + marshaller = method_to_glue_marshal_name(method, 'PREFIX') + + assert '__' in marshaller + rhs = marshaller.split('__', 1)[1].split('_') + + self.marshallers[marshaller] = rhs + + def do_signal(self, signal): + marshaller = signal_to_marshal_name(signal, 'PREFIX') + + assert '__' in marshaller + rhs = marshaller.split('__', 1)[1].split('_') + + self.marshallers[marshaller] = rhs + + def __call__(self): + methods = self.dom.getElementsByTagName('method') + + for method in methods: + self.do_method(method) + + signals = self.dom.getElementsByTagName('signal') + + for signal in signals: + self.do_signal(signal) + + all = self.marshallers.keys() + all.sort() + for marshaller in all: + rhs = self.marshallers[marshaller] + if not marshaller.startswith('g_cclosure'): + print 'VOID:' + ','.join(rhs) + +if __name__ == '__main__': + argv = sys.argv[1:] + dom = xml.dom.minidom.parse(argv[0]) + + Generator(dom)() diff --git a/tools/identity.xsl b/tools/identity.xsl new file mode 100644 index 00000000..6630f84d --- /dev/null +++ b/tools/identity.xsl @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tools/libglibcodegen.py b/tools/libglibcodegen.py new file mode 100644 index 00000000..090e8dec --- /dev/null +++ b/tools/libglibcodegen.py @@ -0,0 +1,320 @@ +"""Library code for GLib/D-Bus-related code generation. + +The master copy of this library is in the telepathy-glib repository - +please make any changes there. +""" + +# Copyright (C) 2006, 2007 Collabora Limited +# +# 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +from string import ascii_letters, digits + + +NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + +_ASCII_ALNUM = ascii_letters + digits + + +def camelcase_to_lower(s): + out =""; + out += s[0].lower() + last_upper=False + if s[0].isupper(): + last_upper=True + for i in range(1,len(s)): + if s[i].isupper(): + if last_upper: + if (i+1) < len(s) and s[i+1].islower(): + out += "_" + s[i].lower() + else: + out += s[i].lower() + else: + out += "_" + s[i].lower() + last_upper=True + else: + out += s[i] + last_upper=False + return out + + +def camelcase_to_upper(s): + return camelcase_to_lower(s).upper() + + +def cmp_by_name(node1, node2): + return cmp(node1.getAttributeNode("name").nodeValue, + node2.getAttributeNode("name").nodeValue) + + +def dbus_gutils_wincaps_to_uscore(s): + """Bug-for-bug compatible Python port of _dbus_gutils_wincaps_to_uscore + which gets sequences of capital letters wrong in the same way. + (e.g. in Telepathy, SendDTMF -> send_dt_mf) + """ + ret = '' + for c in s: + if c >= 'A' and c <= 'Z': + length = len(ret) + if length > 0 and (length < 2 or ret[length-2] != '_'): + ret += '_' + ret += c.lower() + else: + ret += c + return ret + + +def escape_as_identifier(identifier): + """Escape the given string to be a valid D-Bus object path or service + name component, using a reversible encoding to ensure uniqueness. + + The reversible encoding is as follows: + + * The empty string becomes '_' + * Otherwise, each non-alphanumeric character is replaced by '_' plus + two lower-case hex digits; the same replacement is carried out on + the first character, if it's a digit + """ + # '' -> '_' + if not identifier: + return '_' + + # A bit of a fast path for strings which are already OK. + # We deliberately omit '_' because, for reversibility, that must also + # be escaped. + if (identifier.strip(_ASCII_ALNUM) == '' and + identifier[0] in ascii_letters): + return identifier + + # The first character may not be a digit + if identifier[0] not in ascii_letters: + ret = ['_%02x' % ord(identifier[0])] + else: + ret = [identifier[0]] + + # Subsequent characters may be digits or ASCII letters + for c in identifier[1:]: + if c in _ASCII_ALNUM: + ret.append(c) + else: + ret.append('_%02x' % ord(c)) + + return ''.join(ret) + + +def get_docstring(element): + docstring = None + for x in element.childNodes: + if x.namespaceURI == NS_TP and x.localName == 'docstring': + docstring = x + if docstring is not None: + docstring = docstring.toxml().replace('\n', ' ').strip() + if docstring.startswith(''): + docstring = docstring[14:].lstrip() + if docstring.endswith(''): + docstring = docstring[:-15].rstrip() + if docstring in ('', ''): + docstring = '' + return docstring + + +def signal_to_marshal_type(signal): + """ + return a list of strings indicating the marshalling type for this signal. + """ + + mtype=[] + for i in signal.getElementsByTagName("arg"): + name =i.getAttribute("name") + type = i.getAttribute("type") + mtype.append(type_to_gtype(type)[2]) + + return mtype + + +_glib_marshallers = ['VOID', 'BOOLEAN', 'CHAR', 'UCHAR', 'INT', + 'STRING', 'UINT', 'LONG', 'ULONG', 'ENUM', 'FLAGS', 'FLOAT', + 'DOUBLE', 'STRING', 'PARAM', 'BOXED', 'POINTER', 'OBJECT', + 'UINT_POINTER'] + + +def signal_to_marshal_name(signal, prefix): + + mtype = signal_to_marshal_type(signal) + if len(mtype): + name = '_'.join(mtype) + else: + name = 'VOID' + + if name in _glib_marshallers: + return 'g_cclosure_marshal_VOID__' + name + else: + return prefix + '_marshal_VOID__' + name + + +def method_to_glue_marshal_name(method, prefix): + + mtype = [] + for i in method.getElementsByTagName("arg"): + if i.getAttribute("direction") != "out": + type = i.getAttribute("type") + mtype.append(type_to_gtype(type)[2]) + + mtype.append('POINTER') + + name = '_'.join(mtype) + + if name in _glib_marshallers: + return 'g_cclosure_marshal_VOID__' + name + else: + return prefix + '_marshal_VOID__' + name + + +class _SignatureIter: + """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we + can run genginterface in a limited environment with only Python + (like Scratchbox). + """ + def __init__(self, string): + self.remaining = string + + def next(self): + if self.remaining == '': + raise StopIteration + + signature = self.remaining + block_depth = 0 + block_type = None + end = len(signature) + + for marker in range(0, end): + cur_sig = signature[marker] + + if cur_sig == 'a': + pass + elif cur_sig == '{' or cur_sig == '(': + if block_type == None: + block_type = cur_sig + + if block_type == cur_sig: + block_depth = block_depth + 1 + + elif cur_sig == '}': + if block_type == '{': + block_depth = block_depth - 1 + + if block_depth == 0: + end = marker + break + + elif cur_sig == ')': + if block_type == '(': + block_depth = block_depth - 1 + + if block_depth == 0: + end = marker + break + + else: + if block_depth == 0: + end = marker + break + + end = end + 1 + self.remaining = signature[end:] + return Signature(signature[0:end]) + + +class Signature(str): + """A string, iteration over which is by D-Bus single complete types + rather than characters. + """ + def __iter__(self): + return _SignatureIter(self) + + +def type_to_gtype(s): + if s == 'y': #byte + return ("guchar ", "G_TYPE_UCHAR","UCHAR", False) + elif s == 'b': #boolean + return ("gboolean ", "G_TYPE_BOOLEAN","BOOLEAN", False) + elif s == 'n': #int16 + return ("gint ", "G_TYPE_INT","INT", False) + elif s == 'q': #uint16 + return ("guint ", "G_TYPE_UINT","UINT", False) + elif s == 'i': #int32 + return ("gint ", "G_TYPE_INT","INT", False) + elif s == 'u': #uint32 + return ("guint ", "G_TYPE_UINT","UINT", False) + elif s == 'x': #int64 + return ("gint64 ", "G_TYPE_INT64","INT64", False) + elif s == 't': #uint64 + return ("guint64 ", "G_TYPE_UINT64","UINT64", False) + elif s == 'd': #double + return ("gdouble ", "G_TYPE_DOUBLE","DOUBLE", False) + elif s == 's': #string + return ("gchar *", "G_TYPE_STRING", "STRING", True) + elif s == 'g': #signature - FIXME + return ("gchar *", "DBUS_TYPE_G_SIGNATURE", "STRING", True) + elif s == 'o': #object path + return ("gchar *", "DBUS_TYPE_G_OBJECT_PATH", "BOXED", True) + elif s == 'v': #variant + return ("GValue *", "G_TYPE_VALUE", "BOXED", True) + elif s == 'as': #array of strings + return ("gchar **", "G_TYPE_STRV", "BOXED", True) + elif s == 'ay': #byte array + return ("GArray *", + "dbus_g_type_get_collection (\"GArray\", G_TYPE_UCHAR)", "BOXED", + True) + elif s == 'au': #uint array + return ("GArray *", "DBUS_TYPE_G_UINT_ARRAY", "BOXED", True) + elif s == 'ai': #int array + return ("GArray *", "DBUS_TYPE_G_INT_ARRAY", "BOXED", True) + elif s == 'ax': #int64 array + return ("GArray *", "DBUS_TYPE_G_INT64_ARRAY", "BOXED", True) + elif s == 'at': #uint64 array + return ("GArray *", "DBUS_TYPE_G_UINT64_ARRAY", "BOXED", True) + elif s == 'ad': #double array + return ("GArray *", "DBUS_TYPE_G_DOUBLE_ARRAY", "BOXED", True) + elif s == 'ab': #boolean array + return ("GArray *", "DBUS_TYPE_G_BOOLEAN_ARRAY", "BOXED", True) + elif s == 'ao': #object path array + return ("GArray *", "DBUS_TYPE_G_OBJECT_ARRAY", "BOXED", True) + elif s == 'a{ss}': #hash table of string to string + return ("GHashTable *", "DBUS_TYPE_G_STRING_STRING_HASHTABLE", "BOXED", False) + elif s[:2] == 'a{': #some arbitrary hash tables + if s[2] not in ('y', 'b', 'n', 'q', 'i', 'u', 's', 'o', 'g'): + raise Exception, "can't index a hashtable off non-basic type " + s + first = type_to_gtype(s[2]) + second = type_to_gtype(s[3:-1]) + return ("GHashTable *", "(dbus_g_type_get_map (\"GHashTable\", " + first[1] + ", " + second[1] + "))", "BOXED", False) + elif s[:2] in ('a(', 'aa'): # array of structs or arrays, recurse + gtype = type_to_gtype(s[1:])[1] + return ("GPtrArray *", "(dbus_g_type_get_collection (\"GPtrArray\", "+gtype+"))", "BOXED", True) + elif s[:1] == '(': #struct + gtype = "(dbus_g_type_get_struct (\"GValueArray\", " + for subsig in Signature(s[1:-1]): + gtype = gtype + type_to_gtype(subsig)[1] + ", " + gtype = gtype + "G_TYPE_INVALID))" + return ("GValueArray *", gtype, "BOXED", True) + + # we just don't know .. + raise Exception, "don't know the GType for " + s + + +def xml_escape(s): + s = s.replace('&', '&').replace("'", ''').replace('"', '"') + return s.replace('<', '<').replace('>', '>')