summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Crête <olivier.crete@collabora.co.uk>2010-08-15 09:21:23 -0400
committerOlivier Crête <olivier.crete@collabora.co.uk>2010-10-31 14:46:10 -0400
commit0847b44e37bdfc01ea937cdc542ecd880d503c14 (patch)
tree3e726beaf1af4d0b587fda1c86d69cec8e0d758b
parent0c471fe55ac4bbedcc4a1acccb0cc2477a7279f1 (diff)
Import code generator from tp-glib
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac5
-rw-r--r--extensions/Call_Content.xml168
-rw-r--r--extensions/Call_Content_Codec_Offer.xml57
-rw-r--r--extensions/Call_Content_Interface_Media.xml229
-rw-r--r--extensions/Call_Content_Interface_Mute.xml83
-rw-r--r--extensions/Call_Stream.xml178
-rw-r--r--extensions/Call_Stream_Endpoint.xml95
-rw-r--r--extensions/Call_Stream_Interface_Media.xml402
-rw-r--r--extensions/Channel_Type_Call.xml946
-rw-r--r--extensions/Makefile.am174
-rw-r--r--extensions/all.xml12
-rw-r--r--extensions/call-content.c161
-rw-r--r--extensions/call-content.h62
-rw-r--r--extensions/call-content.xml10
-rw-r--r--extensions/call-stream.c161
-rw-r--r--extensions/call-stream.h62
-rw-r--r--extensions/call-stream.xml10
-rw-r--r--extensions/channel.xml9
-rw-r--r--extensions/extensions-cli.c35
-rw-r--r--extensions/extensions.c6
-rw-r--r--extensions/extensions.h27
-rw-r--r--extensions/misc.xml10
-rw-r--r--tools/c-constants-gen.py169
-rw-r--r--tools/glib-client-gen.py1224
-rw-r--r--tools/glib-client-marshaller-gen.py59
-rw-r--r--tools/glib-ginterface-gen.py820
-rw-r--r--tools/glib-gtypes-generator.py298
-rw-r--r--tools/glib-interfaces-gen.py197
-rw-r--r--tools/glib-signals-marshal-gen.py55
-rw-r--r--tools/libglibcodegen.py172
-rw-r--r--tools/libtpcodegen.py215
-rw-r--r--tools/xincludator.py39
33 files changed, 6150 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 203ea7e..883b8aa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,7 +5,7 @@ if WANT_PYTHON
PYTHON_SUBDIR = python
endif
-SUBDIRS = m4 tools telepathy-farsight doc $(PYTHON_SUBDIR)
+SUBDIRS = m4 tools extensions telepathy-farsight doc $(PYTHON_SUBDIR)
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
diff --git a/configure.ac b/configure.ac
index bdefd4a..32e68a2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -109,6 +109,9 @@ PKG_CHECK_MODULES(FARSIGHT2, [farsight2-0.10 >= 0.0.17])
AC_SUBST(FARSIGHT2_CFLAGS)
AC_SUBST(FARSIGHT2_LIBS)
+dnl Always required to generate extensions
+AM_PATH_PYTHON([2.5])
+
AC_ARG_ENABLE([python],
AC_HELP_STRING([--disable-python], [Disable Python bindings]),
[case "${enableval}" in
@@ -119,7 +122,6 @@ AC_ARG_ENABLE([python],
WANT_PYTHON=yes)
if test "x$WANT_PYTHON" = "xyes"; then
- AM_PATH_PYTHON
AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)])
PKG_CHECK_MODULES(PYTPFARSIGHT, [ pygobject-2.0 >= 2.12.0
gst-python-0.10 >= 0.10.10 ])
@@ -140,6 +142,7 @@ AC_OUTPUT( Makefile \
doc/Makefile \
doc/lib/Makefile \
m4/Makefile \
+ extensions/Makefile \
telepathy-farsight/Makefile \
telepathy-farsight/telepathy-farsight.pc \
telepathy-farsight/telepathy-farsight-uninstalled.pc
diff --git a/extensions/Call_Content.xml b/extensions/Call_Content.xml
new file mode 100644
index 0000000..8df078b
--- /dev/null
+++ b/extensions/Call_Content.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" ?>
+<node name="/Call_Content"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Content.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ This object represents one Content inside a Call. For example in an
+ audio/video call there would be one audio and one video content. Each
+ content has one or more <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call">Stream.DRAFT</tp:dbus-ref>
+ objects which represent the actual transport to one or more contacts.
+
+ </tp:docstring>
+
+ <method name="Remove" tp:name-for-bindings="Remove">
+ <tp:docstring>Remove the content from the call.</tp:docstring>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.NetworkError">
+ </tp:error>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented">
+ <tp:docstring>
+ Raised when a Call doesn't support removing contents (e.g. a Google Talk video call)
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <property name="Interfaces" tp:name-for-bindings="Interfaces"
+ type="as" tp:type="DBus_Interface[]" access="read">
+ <tp:added version="0.19.11"/>
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Extra interfaces provided by this content, such as <tp:dbus-ref
+ namespace="ofdT.Call">Content.Interface.Media.DRAFT</tp:dbus-ref> or
+ <tp:dbus-ref
+ namespace="ofdT.Call">Content.Interface.Mute.DRAFT</tp:dbus-ref>.
+ This SHOULD NOT include the Content interface itself, and cannot
+ change once the content has been created.</p>
+ </tp:docstring>
+ </property>
+
+ <property name="Name" tp:name-for-bindings="Name" type="s" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The name of the content.
+ [FIXME: rationale?]</p>
+ </tp:docstring>
+ </property>
+
+ <property name="Type" tp:name-for-bindings="Type"
+ type="u" tp:type="Media_Stream_Type" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The media type of this content</p>
+ </tp:docstring>
+ </property>
+
+ <property name="Creator" tp:name-for-bindings="Creator"
+ type="u" tp:type="Contact_Handle" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The creator of this content</p>
+ </tp:docstring>
+ </property>
+
+ <tp:enum name="Call_Content_Disposition" type="u">
+ <tp:docstring>
+ [FIXME]
+ </tp:docstring>
+
+ <tp:enumvalue suffix="None" value="0">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ The content has no specific disposition
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="Early_Media" value="1">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ [FIXME: what does this mean?]
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="Initial" value="2">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The content was initially part of the call. When <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Type.Call.DRAFT"
+ >Accept</tp:dbus-ref> is called on the channel, all streams of
+ this content where the self-handle's sending state in <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.DRAFT"
+ >Senders</tp:dbus-ref> is Sending_State_Pending_Send
+ will be moved to Sending_State_Sending as if <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.DRAFT"
+ >SetSending</tp:dbus-ref>(TRUE) had been called.</p>
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <property name="Disposition" tp:name-for-bindings="Disposition"
+ type="u" tp:type="Call_Content_Disposition" access="read">
+ <tp:docstring>
+ The disposition of this content. This property cannot change.
+ </tp:docstring>
+ </property>
+
+ <signal name="StreamAdded" tp:name-for-bindings="Stream_Added">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when a stream is added to a call</p>
+ </tp:docstring>
+ <arg name="Stream" type="o">
+ <tp:docstring>
+ The stream which was added
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="StreamRemoved" tp:name-for-bindings="Stream_Removed">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when a stream is added to a call</p>
+ </tp:docstring>
+ <arg name="Stream" type="o">
+ <tp:docstring>
+ The stream which was removed
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="Streams" tp:name-for-bindings="Streams"
+ type="ao" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The list of
+ <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call">Stream.DRAFT</tp:dbus-ref>
+ objects that exist in this content.</p>
+
+ <tp:rationale>
+ <p>In a conference call multiple parties can share one media content
+ (say, audio), but the streaming of that media can either be shared
+ or separate. For example, in a multicast conference all contacts
+ would share one stream, while in a Muji conference there would be
+ a stream for each participant.</p>
+ </tp:rationale>
+
+ <p>Change notification is via
+ <tp:member-ref>StreamAdded</tp:member-ref> and
+ <tp:member-ref>StreamRemoved</tp:member-ref>.</p>
+ </tp:docstring>
+ </property>
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Call_Content_Codec_Offer.xml b/extensions/Call_Content_Codec_Offer.xml
new file mode 100644
index 0000000..31ff0b3
--- /dev/null
+++ b/extensions/Call_Content_Codec_Offer.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" ?>
+<node name="/Call_Content_Codec_Offer"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Content.CodecOffer.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ This object represents an offer of a Codec payload mapping.
+ FIXME add Accept and Reject signals ?
+ </tp:docstring>
+
+ <method name="Accept" tp:name-for-bindings="Accept">
+ <arg name="Codecs" direction="in"
+ type="a(usuua{ss})" tp:type="Codec[]" />
+ <tp:docstring>
+ Accept the updated Codec mapping and update the local mapping
+ </tp:docstring>
+ </method>
+
+ <method name="Reject" tp:name-for-bindings="Reject">
+ <tp:docstring>
+ Reject the proposed update to the codecs
+ FIXME add error codes and strings here
+ </tp:docstring>
+ </method>
+
+ <property name="RemoteContactCodecMap"
+ tp:name-for-bindings="Remote_Contact_Codec_Map"
+ type="a{ua(usuua{ss})}" tp:type="Contact_Codec_Map" access="read">
+ <tp:docstring>
+ A map from remote contacts to the lists of codecs they support.
+ </tp:docstring>
+ </property>
+
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Call_Content_Interface_Media.xml b/extensions/Call_Content_Interface_Media.xml
new file mode 100644
index 0000000..8b9a17c
--- /dev/null
+++ b/extensions/Call_Content_Interface_Media.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" ?>
+<node name="/Call_Content_Interface_Media"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Content.Interface.Media.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+ <tp:requires interface="org.freedesktop.Telepathy.Call.Content"/>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ Interface to use by a software implementation of media streaming.
+
+ FIXME: How should the streaming implementation know when it is its turn
+ to set the codecs.
+ </tp:docstring>
+
+ <tp:struct name="Codec" array-name="Codec_List">
+ <tp:docstring>
+ A description of a codec.
+ </tp:docstring>
+
+ <tp:member name="Identifier" type="u">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ Numeric identifier for the codec. This will be used as the PT in the
+ SDP or content description.
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member name="Name" type="s">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ The name of the codec.
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member name="Clockrate" type="u">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ The clockrate of the codec
+ </tp:docstring>
+ </tp:member>
+ <tp:member name="Channels" type="u">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ Number of channels of the codec if applicable, otherwise 0
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member name="Parameters" type="a{ss}" tp:type="String_String_Map">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ Extra parameters for this codec
+ </tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <tp:mapping name="Contact_Codec_Map">
+ <tp:docstring>
+ A map from contacts to the lists of codecs they support.
+ </tp:docstring>
+
+ <tp:member name="Handle" type="u" tp:type="Contact_Handle">
+ <tp:docstring>
+ A contact
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member name="Codecs" type="a(usuua{ss})" tp:type="Codec[]">
+ <tp:docstring>
+ The codecs that the contact supports
+ </tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ <tp:struct name="Codec_Offering">
+ <tp:docstring>
+ A codec offer and its corresponding remote contact codec map.
+ </tp:docstring>
+
+ <tp:member name="Codec_Offer" type="o">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ The object path to the
+ <tp:dbus-ref namespace="org.freedesktop.Telepathy.Call.Content"
+ >CodecOffer.DRAFT</tp:dbus-ref>
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member name="Remote_Contact_Codec_Map" type="a{ua(usuua{ss})}"
+ tp:type="Contact_Codec_Map">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ The <tp:dbus-ref namespace="org.freedesktop.Telepathy.Call.Content"
+ >CodecOffer.DRAFT.RemoteContactCodecMap</tp:dbus-ref> property
+ of the codec offer.
+ </tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <signal name="CodecsChanged" tp:name-for-bindings="Codecs_Changed">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when the codecs in use change.</p>
+
+ <p>As well as acting as change notification for the
+ <tp:member-ref>ContactCodecMap</tp:member-ref>, emission of this
+ signal implies that the <tp:member-ref>CodecOffer</tp:member-ref>
+ property has changed to <code>('/', {})</code>.</p>
+ </tp:docstring>
+
+ <arg name="Updated_Codecs" type="a{ua(usuua{ss})}"
+ tp:type="Contact_Codec_Map">
+ <tp:docstring>
+ A map from contacts to their codecs. Each pair in this map is added
+ to the <tp:member-ref>ContactCodecMap</tp:member-ref> property,
+ replacing any previous pair with that key.
+ </tp:docstring>
+ </arg>
+
+ <arg name="Removed_Contacts" type="au" tp:type="Contact_Handle[]">
+ <tp:docstring>
+ A list of keys which were removed from the
+ <tp:member-ref>ContactCodecMap</tp:member-ref>, probably because
+ those contacts left the call.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <method name="SetCodecs" tp:name-for-bindings="Set_Codecs">
+ <tp:docstring>
+ Set or update the local codec mapping.
+ </tp:docstring>
+
+ <arg name="Codecs" direction="in"
+ type="a(usuua{ss})" tp:type="Codec[]">
+ <tp:docstring>
+ The codecs now supported by the local user.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <property name="ContactCodecMap" tp:name-for-bindings="Contact_Codec_Map"
+ type="a{ua(usuua{ss})}" tp:type="Contact_Codec_Map" access="read">
+ <tp:docstring>
+ <p>A map from contact handles (including the local user's own handle)
+ to the codecs supported by that contact.</p>
+
+ <p>Change notification is via
+ <tp:member-ref>CodecsChanged</tp:member-ref>.</p>
+ </tp:docstring>
+ </property>
+
+ <signal name="NewCodecOffer" tp:name-for-bindings="New_Codec_Offer">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when a new <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Content"
+ >CodecOffer.DRAFT</tp:dbus-ref> appears. The streaming
+ implementation MUST respond by calling the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Content.CodecOffer.DRAFT"
+ >Accept</tp:dbus-ref> or <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Content.CodecOffer.DRAFT"
+ >Reject</tp:dbus-ref> method on the codec offer object.</p>
+
+ <p>Emission of this signal indicates that the
+ <tp:member-ref>CodecOffer</tp:member-ref> property has changed to
+ <code>(Offer, Codecs)</code>.</p>
+ </tp:docstring>
+
+ <arg name="Offer" type="o">
+ <tp:docstring>
+ The object path of the new codec offer. This replaces any previous
+ codec offer.
+ </tp:docstring>
+ </arg>
+
+ <arg name="Codecs" type="a{ua(usuua{ss})}" tp:type="Contact_Codec_Map">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The <tp:dbus-ref namespace="org.freedesktop.Telepathy.Call.Content"
+ >CodecOffer.DRAFT.RemoteContactCodecMap</tp:dbus-ref> property
+ of the codec offer.</p>
+
+ <tp:rationale>
+ <p>Having the RemoteContactCodecMap property here saves a D-Bus
+ round-trip - it shouldn't be necessary to get the property
+ from the CodecOffer object, in practice.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="CodecOffer" tp:name-for-bindings="Codec_Offer"
+ type="(oa{ua(usuua{ss})})" tp:type="Codec_Offering" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The object path to the current
+ <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Content"
+ >CodecOffer.DRAFT</tp:dbus-ref> object, and its
+ <tp:dbus-ref namespace="org.freedesktop.Telepathy.Call.Content"
+ >CodecOffer.DRAFT.RemoteContactCodecMap</tp:dbus-ref> property.
+ If the object path is "/" then there isn't an outstanding
+ codec offer, and the mapping MUST be empty.</p>
+
+ <tp:rationale>
+ <p>Having the RemoteContactCodecMap property here saves a D-Bus
+ round-trip - it shouldn't be necessary to get the property
+ from the CodecOffer object, in practice.</p>
+ </tp:rationale>
+
+ <p>Change notification is via
+ <tp:member-ref>NewCodecOffer</tp:member-ref> (which replaces the
+ value of this property with a new codec offer), and
+ <tp:member-ref>CodecsChanged</tp:member-ref> (which implies that
+ there is no longer any active codec offer).</p>
+ </tp:docstring>
+ </property>
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Call_Content_Interface_Mute.xml b/extensions/Call_Content_Interface_Mute.xml
new file mode 100644
index 0000000..eea724f
--- /dev/null
+++ b/extensions/Call_Content_Interface_Mute.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" ?>
+<node name="/Call_Content_Interface_Mute" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright> Copyright © 2005-2010 Nokia Corporation </tp:copyright>
+ <tp:copyright> Copyright © 2005-2010 Collabora Ltd </tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Content.Interface.Mute.DRAFT" tp:causes-havoc="experimental">
+ <tp:added version="0.19.6">(draft version, not API-stable)</tp:added>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Interface for calls which may be muted. This only makes sense
+ for channels where audio or video is streaming between members.</p>
+
+ <p>Muting a call content indicates that the user does not wish to send
+ outgoing audio or video.</p>
+
+ <p>Although it's client's responsibility to actually mute the microphone
+ or turn off the camera, using this interface the client can also
+ inform the CM and other clients of that fact.</p>
+ <tp:rationale>
+ <p>For some protocols, the fact that the content is muted needs to be
+ transmitted to the peer; for others, the notification to the peer is
+ only informational (eg. XMPP), and some protocols may have no notion
+ of muting at all.</p>
+ </tp:rationale>
+ </tp:docstring>
+
+ <signal name="MuteStateChanged" tp:name-for-bindings="Mute_State_Changed">
+ <tp:docstring>
+ Emitted to indicate that the mute state has changed for this call content.
+ This may occur as a consequence of the client calling
+ <tp:member-ref>Muted</tp:member-ref>, or as an indication that another
+ client has (un)muted the content.
+ </tp:docstring>
+
+ <arg name="MuteState" type="b">
+ <tp:docstring>
+ True if the content is now muted.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="MuteState" type="b"
+ access="read" tp:name-for-bindings="Mute_State">
+ <tp:docstring>
+ True if the content is muted.
+ </tp:docstring>
+ </property>
+
+ <method name="Muted" tp:name-for-bindings="Muted">
+ <arg direction="in" name="Muted" type="b">
+ <tp:docstring>
+ True if the client has muted the content.
+ </tp:docstring>
+ </arg>
+
+ <tp:docstring>
+ <p>Inform the CM that the call content has been muted or unmuted by
+ che client.</p>
+
+ <p>It is the client's responsibility to actually mute or unmute the
+ microphone or camera used for the content. However, the client
+ MUST call this whenever it mutes or unmutes the content.</p>
+ </tp:docstring>
+ </method>
+
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Call_Stream.xml b/extensions/Call_Stream.xml
new file mode 100644
index 0000000..0fbb9c8
--- /dev/null
+++ b/extensions/Call_Stream.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0" ?>
+<node name="/Call_Stream"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Stream.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ One stream inside a content
+ FIXME, direction should be a mapping of contact -> (bool)sending ?
+ </tp:docstring>
+
+ <method name="SetSending" tp:name-for-bindings="Set_Sending">
+ <tp:docstring>
+ Set the stream to start or stop sending media from the local
+ user to other contacts.
+ </tp:docstring>
+
+ <arg name="Send" type="b" direction="in">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>If true, the local user's sending state should change
+ to Sending, if it isn't already.</p>
+
+ <p>If false, the local user's sending state should change to None,
+ if it isn't already.</p>
+ </tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented">
+ <tp:docstring>
+ [FIXME: when?]
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <method name="RequestReceiving" tp:name-for-bindings="Request_Receiving">
+ <tp:docstring>
+ Request that a remote contact stops or starts sending on this stream.
+ </tp:docstring>
+
+ <arg name="Contact" type="u" tp:type="Contact_Handle" direction="in">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Contact from which sending is requested</p>
+ </tp:docstring>
+ </arg>
+
+ <arg name="Receive" type="b" direction="in">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>If true, request that the given contact starts to send media.
+ If false, request that the given contact stops sending media.</p>
+ </tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented">
+ <tp:docstring>
+ [FIXME: when?]
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <signal name="SendersChanged"
+ tp:name-for-bindings="Senders_Changed">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ Emitted when <tp:member-ref>Senders</tp:member-ref> changes.
+ </tp:docstring>
+
+ <arg name="Updates" type="a{uu}" tp:type="Contact_Sending_State_Map">
+ <tp:docstring>
+ A mapping from channel-specific handles to their updated sending
+ state, whose keys include at least the senders who were added,
+ and the senders whose states changed.
+ </tp:docstring>
+ </arg>
+ <arg name="Removed" type="au" tp:type="Contact_Handle[]">
+ <tp:docstring>
+ The channel-specific handles that were removed from
+ the keys of the Senders property, as a result of the
+ contact leaving this stream
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <tp:enum name="Sending_State" type="u">
+ <tp:docstring>
+ Tristate indicating whether a contact is sending media.
+ </tp:docstring>
+
+ <tp:enumvalue suffix="None" value="0">
+ <tp:docstring>
+ The contact is not sending media and has not been asked to do so.
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="Pending_Send" value="1">
+ <tp:docstring>
+ The contact has been asked to start sending media.
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="Sending" value="2">
+ <tp:docstring>
+ The contact is sending media.
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <tp:mapping name="Contact_Sending_State_Map">
+ <tp:docstring>
+ A map from contacts to their sending state.
+ </tp:docstring>
+ <tp:member name="Contact" type="u" tp:type="Contact_Handle">
+ </tp:member>
+ <tp:member name="Sending" type="u" tp:type="Sending_State">
+ <tp:docstring>
+ </tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ <property name="Interfaces" tp:name-for-bindings="Interfaces"
+ type="as" tp:type="DBus_Interface[]" access="read">
+ <tp:added version="0.19.11"/>
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Extra interfaces provided by this stream, such as <tp:dbus-ref
+ namespace="ofdT.Call">Stream.Interface.Media.DRAFT</tp:dbus-ref>.
+ This SHOULD NOT include the Stream interface itself, and cannot
+ change once the stream has been created.</p>
+ </tp:docstring>
+ </property>
+
+ <property name="Senders" tp:name-for-bindings="Senders"
+ type="a{uu}" access="read" tp:type="Contact_Sending_State_Map">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A map from contacts to their sending state.</p>
+
+ <p>The local user's handle in this map (the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Interface"
+ >Group.SelfHandle</tp:dbus-ref> if the channel implements
+ Group, or the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy"
+ >Connection.SelfHandle</tp:dbus-ref> otherwise) indicates
+ whether the local user is sending media. Media sent on this stream
+ should be assumed to be received, directly or indirectly, by every
+ other contact in the Senders mapping. Sending_State_Pending_Send
+ indicates that another contact has asked the local user to send
+ media.</p>
+
+ <p>Other contacts' handles in this map indicate whether they are
+ sending media to the contacts in this stream.
+ Sending_State_Pending_Send indicates contacts who are not sending but
+ have been asked to do so.</p>
+ </tp:docstring>
+ </property>
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Call_Stream_Endpoint.xml b/extensions/Call_Stream_Endpoint.xml
new file mode 100644
index 0000000..fbab2cf
--- /dev/null
+++ b/extensions/Call_Stream_Endpoint.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" ?>
+<node name="/Call_Stream_Endpoint"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Stream.Endpoint.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ This object represents a set of candidates of one end-point.
+ </tp:docstring>
+
+ <property name="RemoteCredentials"
+ tp:name-for-bindings="Remote_Credentials"
+ type="(ss)" tp:type="Stream_Credentials" access="read">
+ </property>
+
+ <signal name="RemoteCredentialsSet"
+ tp:name-for-bindings="Remote_Credentials_Set">
+ <arg name="Username" type="s" />
+ <arg name="Password" type="s" />
+ </signal>
+
+ <property name="RemoteCandidates" tp:name-for-bindings="Remote_Candidates"
+ type="a(usqa{sv})" tp:type="Candidate[]" access="read">
+ </property>
+
+ <signal name="RemoteCandidatesAdded"
+ tp:name-for-bindings="Remote_Candidates_Added">
+ <arg name="Candidates"
+ type="a(usqa{sv})" tp:type="Candidate[]"/>
+ </signal>
+
+ <signal name="CandidateSelected"
+ tp:name-for-bindings="Candidate_Selected">
+ <arg name="Candidate"
+ type="(usqa{sv})" tp:type="Candidate"/>
+ </signal>
+
+ <property name="SelectedCandidate"
+ tp:name-for-bindings="Selected_Candidate"
+ type="(usqa{sv})" tp:type="Candidate" access="read">
+ </property>
+
+ <method name="SetSelectedCandidate"
+ tp:name-for-bindings="Set_Selected_Candidate">
+ <arg name="Candidate"
+ type="(usqa{sv})" tp:type="Candidate" direction="in">
+ <tp:docstring>
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <property name="StreamState" tp:name-for-bindings="Stream_State"
+ type="u" tp:type="Media_Stream_State"
+ access="read">
+ </property>
+
+ <signal name="StreamStateChanged"
+ tp:name-for-bindings="Stream_State_Changed">
+ <arg name="state"
+ type="u" tp:type="Media_Stream_State"/>
+ </signal>
+
+ <method name="SetStreamState"
+ tp:name-for-bindings="Set_Stream_State">
+ <arg name="State" type="u" tp:type="Media_Stream_State"
+ direction="in" />
+ </method>
+
+ <property name="Transport" tp:name-for-bindings="Transport"
+ type="u" tp:type="Stream_Transport_Type" access="read">
+ </property>
+
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Call_Stream_Interface_Media.xml b/extensions/Call_Stream_Interface_Media.xml
new file mode 100644
index 0000000..b1b9e19
--- /dev/null
+++ b/extensions/Call_Stream_Interface_Media.xml
@@ -0,0 +1,402 @@
+<?xml version="1.0" ?>
+<node name="/Call_Stream_Interface_Media"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Ltd.</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>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.</p>
+
+ <p>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.</p>
+
+ <p>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.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Call.Stream.Interface.Media.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+ <tp:requires interface="org.freedesktop.Telepathy.Call.Stream"/>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ [FIXME]
+ </tp:docstring>
+
+ <tp:method name="SetCredentials" tp:name-for-bindings="Set_Credentials">
+ <tp:docstring>
+ Used to set the username fragment and password for streams that have
+ global credentials.
+
+ <tp:rationale>
+ [FIXME: rationale?]
+ </tp:rationale>
+ </tp:docstring>
+ <arg name="Username" type="s" direction="in"/>
+ <arg name="Password" type="s" direction="in" />
+ </tp:method>
+
+ <tp:mapping name="Candidate_Info">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ Extra information about the candidate. Allowed and mandatory keys
+ depend on the transport protocol used. The following keys are commenly
+ used:
+ <dl>
+ <dt>Type (u)</dt>
+ <dd>type of candidate (host, srflx, prflx, relay)</dd>
+
+ <dt>Foundation (s)</dt>
+ <dd>the foundation of this candiate</dd>
+
+ <dt>Protocol (u) </dt>
+ <dd>Underlying protocol of the candidate (udp, tcp) </dd>
+
+ <dt>Priority (u) </dt>
+ <dd>Priority of the candidate </dd>
+
+ <dt>BaseIP (u) </dt>
+ <dd>Base IP of this candidate </dd>
+
+ <dt>Username (s) </dt>
+ <dd>Username of this candidate
+ (only if credentials are per candidate)</dd>
+
+ <dt>Password (s) </dt>
+ <dd>Password of this candidate
+ (only if credentials are per candidate)</dd>
+
+ <dt>RawUDPFallback (b) </dt>
+ <dd>Indicate whether this candidate may be used to provide a UDP
+ fallback</dd>
+ </dl>
+ </tp:docstring>
+ <tp:member name="Key" type="s">
+ <tp:docstring>One of the well-known keys documented here, or an
+ implementation-specific key</tp:docstring>
+ </tp:member>
+ <tp:member name="Value" type="v">
+ <tp:docstring>The value corresponding to that key</tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ <tp:struct name="Candidate" array-name="Candidate_List">
+ <tp:docstring>A Stream Candidate</tp:docstring>
+
+ <tp:member name="Component" type="u">
+ <tp:docstring>The component number</tp:docstring>
+ </tp:member>
+ <tp:member name="IP" type="s">
+ <tp:docstring>The IP address to use</tp:docstring>
+ </tp:member>
+ <tp:member name="Port" type="q">
+ <tp:docstring>The port number to use</tp:docstring>
+ </tp:member>
+ <tp:member name="Info" type="a{sv}" tp:type="Candidate_Info">
+ <tp:docstring>Additional information about the candidate</tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <method name="AddCandidates" tp:name-for-bindings="Add_Candidates">
+ <tp:docstring>
+ Add candidates to <tp:member-ref>LocalCandidates</tp:member-ref>
+ and signal them to the remote contact(s).
+ </tp:docstring>
+
+ <arg name="candidates" direction="in"
+ type="a(usqa{sv})" tp:type="Candidate[]">
+ <tp:docstring>
+ Candidates to be appended to
+ <tp:member-ref>LocalCandidates</tp:member-ref>
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <method name="CandidatesPrepared"
+ tp:name-for-bindings="Candidates_Prepared">
+ <tp:docstring>
+ This indicates to the CM that the initial batch of candidates has been
+ added.
+
+ <tp:rationale>
+ [FIXME: rationale]
+ </tp:rationale>
+ </tp:docstring>
+ </method>
+
+ <tp:enum type="u" name="Stream_Transport_Type">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ A transport that can be used for streaming.
+ </tp:docstring>
+
+ <tp:enumvalue suffix="Raw_UDP" value="0">
+ <tp:docstring>
+ Raw UDP, with or without STUN. All streaming clients are assumed to
+ support this transport, so there is no handler capability token for
+ it in the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Type"
+ >Call.DRAFT</tp:dbus-ref> interface.
+ [This corresponds to "none" or "stun" in the old Media.StreamHandler
+ interface.]
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="ICE" value="1">
+ <tp:docstring>
+ Interactive Connectivity Establishment, as defined by the IETF MMUSIC
+ working group.
+ [FIXME: do we want this to cover both ICE-UDP and ICE-TCP, or split
+ them?]
+ [This corresponds to "ice-udp" in the old Media.StreamHandler
+ interface.]
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="GTalk_P2P" value="2">
+ <tp:docstring>
+ Google Talk peer-to-peer connectivity establishment, as implemented
+ by libjingle 0.3.
+ [This corresponds to "gtalk-p2p" in the old Media.StreamHandler
+ interface.]
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="WLM_8_5" value="3">
+ <tp:docstring>
+ The transport used by Windows Live Messenger 8.5 or later, which
+ resembles ICE draft 6.
+ [This corresponds to "wlm-8.5" in the old Media.StreamHandler
+ interface.]
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="WLM_2009" value="4">
+ <tp:docstring>
+ The transport used by Windows Live Messenger 2009 or later, which
+ resembles ICE draft 19.
+ [This corresponds to "wlm-2009" in the old Media.StreamHandler
+ interface.]
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <property name="Transport" tp:name-for-bindings="Transport"
+ type="u" tp:type="Stream_Transport_Type" access="read">
+ <tp:docstring>
+ The transport for this stream. This property is immutable.
+ </tp:docstring>
+ </property>
+
+ <property name="LocalCandidates" tp:name-for-bindings="Local_Candidates"
+ type="a(usqa{sv})" tp:type="Candidate[]" access="read">
+ <tp:docstring>
+ [FIXME]. Change notification is via
+ <tp:member-ref>LocalCandidatesAdded</tp:member-ref>.
+ </tp:docstring>
+ </property>
+
+ <signal name="LocalCandidatesAdded"
+ tp:name-for-bindings="Local_Candidates_Added">
+ <tp:docstring>
+ Emitted when local candidates are added to
+ <tp:member-ref>LocalCandidates</tp:member-ref>.
+ </tp:docstring>
+
+ <arg name="Candidates"
+ type="a(usqa{sv})" tp:type="Candidate[]">
+ <tp:docstring>
+ Candidates that have been appended to
+ <tp:member-ref>LocalCandidates</tp:member-ref>
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <tp:struct name="Stream_Credentials">
+ <tp:docstring>A username/password pair.</tp:docstring>
+
+ <tp:member name="Username" type="s">
+ <tp:docstring>The username</tp:docstring>
+ </tp:member>
+
+ <tp:member name="Password" type="s">
+ <tp:docstring>The password</tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <property name="LocalCredentials" tp:name-for-bindings="Local_Credentials"
+ type="(ss)" tp:type="Stream_Credentials" access="read">
+ <tp:docstring>
+ [FIXME]. Change notification is via
+ <tp:member-ref>LocalCredentialsSet</tp:member-ref>.
+ </tp:docstring>
+
+ </property>
+
+ <signal name="LocalCredentialsSet"
+ tp:name-for-bindings="Local_Credentials_Set">
+ <tp:docstring>
+ Emitted when the value of
+ <tp:member-ref>LocalCredentials</tp:member-ref> changes.
+ </tp:docstring>
+
+ <arg name="Username" type="s" />
+ <arg name="Password" type="s" />
+ </signal>
+
+ <property name="STUNServers" tp:name-for-bindings="STUN_Servers"
+ type="a(sq)" tp:type="Socket_Address_IP[]" access="read">
+ <tp:docstring>
+ The IP addresses of possible STUN servers to use for NAT traversal, as
+ dotted-quad IPv4 address literals or RFC2373 IPv6 address literals.
+ This property cannot change once the stream has been created, so there
+ is no change notification. The IP addresses MUST NOT be given as DNS
+ hostnames.
+
+ <tp:rationale>
+ High-quality connection managers already need an asynchronous
+ DNS resolver, so they might as well resolve this name to an IP
+ to make life easier for streaming implementations.
+ </tp:rationale>
+ </tp:docstring>
+ </property>
+
+ <property name="RelayInfo" type="aa{sv}" access="read"
+ tp:type="String_Variant_Map[]" tp:name-for-bindings="Relay_Info">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A list of mappings describing TURN or Google relay servers
+ available for the client to use in its candidate gathering, as
+ determined from the protocol. Map keys are:</p>
+
+ <dl>
+ <dt><code>ip</code> - s</dt>
+ <dd>The IP address of the relay server as a dotted-quad IPv4
+ address literal or an RFC2373 IPv6 address literal. This MUST NOT
+ be a DNS hostname.
+
+ <tp:rationale>
+ High-quality connection managers already need an asynchronous
+ DNS resolver, so they might as well resolve this name to an IP
+ and make life easier for streaming implementations.
+ </tp:rationale>
+ </dd>
+
+ <dt><code>type</code> - s</dt>
+ <dd>
+ <p>Either <code>udp</code> for UDP (UDP MUST be assumed if this
+ key is omitted), <code>tcp</code> for TCP, or
+ <code>tls</code>.</p>
+
+ <p>The precise meaning of this key depends on the
+ <tp:member-ref>Transport</tp:member-ref> property: if
+ Transport is ICE, <code>tls</code> means
+ TLS over TCP as referenced by ICE draft 19, and if
+ Transport is GTalk_P2P, <code>tls</code> means
+ a fake SSL session over TCP as implemented by libjingle.</p>
+ </dd>
+
+ <dt><code>port</code> - q</dt>
+ <dd>The UDP or TCP port of the relay server as an ASCII unsigned
+ integer</dd>
+
+ <dt><code>username</code> - s</dt>
+ <dd>The username to use</dd>
+
+ <dt><code>password</code> - s</dt>
+ <dd>The password to use</dd>
+
+ <dt><code>component</code> - u</dt>
+ <dd>The component number to use this relay server for, as an
+ ASCII unsigned integer; if not included, this relay server
+ may be used for any or all components.
+
+ <tp:rationale>
+ In ICE draft 6, as used by Google Talk, credentials are only
+ valid once, so each component needs relaying separately.
+ </tp:rationale>
+ </dd>
+ </dl>
+
+ <tp:rationale>
+ <p>An equivalent of the gtalk-p2p-relay-token property on
+ MediaSignalling channels is not included here. The connection
+ manager should be responsible for making the necessary HTTP
+ requests to turn the token into a username and password.</p>
+ </tp:rationale>
+
+ <p>The type of relay server that this represents depends on
+ the value of the <tp:member-ref>Transport</tp:member-ref>
+ property. If Transport is ICE, this is a TURN server;
+ if Transport is GTalk_P2P, this is a Google relay server;
+ otherwise, the meaning of RelayInfo is undefined.</p>
+
+ <p>If relaying is not possible for this stream, the list is empty.</p>
+ </tp:docstring>
+ </property>
+
+ <signal name="ServerInfoRetrieved"
+ tp:name-for-bindings="Server_Info_Retrieved">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Signals that the initial information about STUN and Relay servers
+ has been retrieved, i.e. the
+ <tp:member-ref>RetrievedServerInfo</tp:member-ref> property is now
+ true.</p>
+ </tp:docstring>
+ </signal>
+
+ <property name="RetrievedServerInfo" type="b"
+ tp:name-for-bindings="Retrieved_Server_Info" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>True if the initial information about STUN servers and Relay servers
+ has been retrieved. Change notification is via the
+ <tp:member-ref>ServerInfoRetrieved</tp:member-ref> signal.</p>
+
+ <tp:rationale>
+ <p>Streaming implementations that can't cope with STUN and relay
+ servers being added later SHOULD wait for this property
+ to become true before proceeding.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </property>
+
+ <signal name="EndpointsChanged"
+ tp:name-for-bindings="Endpoints_Changed">
+ <tp:docstring>
+ Emitted when the <tp:member-ref>Endpoints</tp:member-ref> property
+ changes.
+ </tp:docstring>
+
+ <arg name="EndpointsAdded" type="ao">
+ <tp:docstring>
+ Endpoints that were added.
+ </tp:docstring>
+ </arg>
+
+ <arg name="EndpointsRemoved" type="ao">
+ <tp:docstring>
+ Endpoints that no longer exist.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="Endpoints" tp:name-for-bindings="Endpoints"
+ type="ao" access="read">
+ <tp:docstring>
+ <p> The list of endpoints
+ <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream"
+ >Endpoint.DRAFT</tp:dbus-ref>
+ that exist for this stream.
+ </p>
+
+ <p>Change notification is via the
+ <tp:member-ref>EndpointsChanged</tp:member-ref> signal.</p>
+ </tp:docstring>
+ </property>
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Channel_Type_Call.xml b/extensions/Channel_Type_Call.xml
new file mode 100644
index 0000000..f1d0f0e
--- /dev/null
+++ b/extensions/Channel_Type_Call.xml
@@ -0,0 +1,946 @@
+<?xml version="1.0" ?>
+<node name="/Channel_Type_Call" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright © 2009 Collabora Limited</tp:copyright>
+ <tp:copyright>Copyright © 2009 Nokia Corporation</tp:copyright>
+ <tp:license>
+ 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ </tp:license>
+ <interface name="org.freedesktop.Telepathy.Channel.Type.Call.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.19.0">(draft 1)</tp:added>
+
+ <tp:requires interface="org.freedesktop.Telepathy.Channel"/>
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A channel type for making audio and video calls.</p>
+
+ <p>A Call channel can have one or more <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call">Content.DRAFT</tp:dbus-ref>
+ objects, which represent the actual Media that forms the Call (e.g. an
+ audio content and a video content).</p>
+ </tp:docstring>
+
+ <method name="Ringing" tp:name-for-bindings="Ringing">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Indicate that the local user has been alerted about the incoming
+ call.</p>
+
+ <p>This method is only useful if the channel's
+ <tp:dbus-ref namespace="org.freedesktop.Telepathy.Channel"
+ >Requested</tp:dbus-ref> property is false, and the
+ <tp:member-ref>CallState</tp:member-ref> is
+ Call_State_Pending_Receiver. While this is the case,
+ this method SHOULD change the
+ <tp:member-ref>CallFlags</tp:member-ref> to include
+ Call_Flag_Ringing, and notify the remote contact that the local
+ user has been alerted (if the protocol implements this); repeated
+ calls to this method SHOULD succeed, but have no further effect.</p>
+
+ <p>In all other states, this method SHOULD fail with the error
+ NotAvailable.</p>
+ </tp:docstring>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument">
+ <tp:docstring>
+ The call was <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel"
+ >Requested</tp:dbus-ref>, so ringing does not make sense.
+ </tp:docstring>
+ </tp:error>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable">
+ <tp:docstring>
+ The call is no longer in state Call_State_Pending_Receiver.
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <method name="Accept" tp:name-for-bindings="Accept">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>For incoming calls in state Call_State_Pending_Receiver, accept the
+ incoming call; this changes the
+ <tp:member-ref>CallState</tp:member-ref> to Call_State_Accepted.</p>
+
+ <p>For outgoing calls in state Call_State_Pending_Initiator, actually
+ call the remote contact; this changes the
+ <tp:member-ref>CallState</tp:member-ref> to
+ Call_State_Pending_Receiver.</p>
+
+ <p>Otherwise, this method SHOULD fail with the error NotAvailable.</p>
+
+ <p>This method should be called exactly once per Call, by whatever
+ client (user interface) is handling the channel.</p>
+
+ <p>When this method is called, for each <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call"
+ >Content.DRAFT</tp:dbus-ref> whose <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Content.DRAFT"
+ >Disposition</tp:dbus-ref> is Call_Content_Disposition_Initial,
+ any streams where the self-handle's sending state in <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.DRAFT"
+ >Senders</tp:dbus-ref> is Sending_State_Pending_Send
+ will be moved to Sending_State_Sending as if <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.DRAFT"
+ >SetSending</tp:dbus-ref>(TRUE) had been called.</p>
+ </tp:docstring>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable">
+ <tp:docstring>
+ The call is not in one of the states where this method makes sense.
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <method name="Hangup" tp:name-for-bindings="Hangup">
+ <tp:docstring>
+ Request that the call is ended.
+ </tp:docstring>
+
+ <arg direction="in" name="Reason"
+ type="u" tp:type="Call_State_Change_Reason">
+ <tp:docstring>
+ A generic hangup reason.
+ </tp:docstring>
+ </arg>
+
+ <arg direction="in" name="Detailed_Hangup_Reason"
+ type="s" tp:type="DBus_Error_Name">
+ <tp:docstring>
+ A more specific reason for the call hangup, if one is available, or
+ an empty string otherwise.
+ </tp:docstring>
+ </arg>
+
+ <arg direction="in" name="Message" type="s">
+ <tp:docstring>
+ A human-readable message to be sent to the remote contact(s).
+
+ <tp:rationale>
+ XMPP Jingle allows calls to be terminated with a human-readable
+ message.
+ </tp:rationale>
+ </tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable">
+ <tp:docstring>
+ The call has already been ended.
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <method name="AddContent" tp:name-for-bindings="Add_Content">
+ <tp:docstring>
+ [FIXME]
+ </tp:docstring>
+ <arg direction="in" name="Content_Name" type="s">
+ <tp:docstring>
+ The suggested name of the content to add
+
+ <tp:rationale>
+ [FIXME: rationale]
+ </tp:rationale>
+ </tp:docstring>
+ </arg>
+ <arg direction="in" name="Content_Type" type="u"
+ tp:type="Media_Stream_Type">
+ <tp:docstring>
+ The media type of the content to add
+ </tp:docstring>
+ </arg>
+ <arg direction="out" name="Content" type="o">
+ <tp:docstring>
+ Path to the newly-created <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy"
+ >Call.Content.DRAFT</tp:dbus-ref> object.
+ </tp:docstring>
+ </arg>
+
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument">
+ <tp:docstring>
+ [FIXME: when?]
+ </tp:docstring>
+ </tp:error>
+ <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented">
+ <tp:docstring>
+ [FIXME: when?]
+ </tp:docstring>
+ </tp:error>
+ </tp:possible-errors>
+ </method>
+
+ <signal name="ContentAdded"
+ tp:name-for-bindings="Content_Added">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when a new <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call"
+ >Content.DRAFT</tp:dbus-ref> is added to the call.</p>
+ </tp:docstring>
+ <arg name="Content" type="o">
+ <tp:docstring>
+ Path to the newly-created <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call"
+ >Content.DRAFT</tp:dbus-ref> object.
+ </tp:docstring>
+ </arg>
+ <arg name="Content_Type" type="u" tp:type="Media_Stream_Type">
+ <tp:docstring>
+ The media type of the content which was added
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="ContentRemoved" tp:name-for-bindings="Content_Removed">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when a <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call"
+ >Content.DRAFT</tp:dbus-ref> is removed from the call.</p>
+ </tp:docstring>
+ <arg name="Content" type="o">
+ <tp:docstring>
+ The <tp:dbus-ref namespace="org.freedesktop.Telepathy.Call"
+ >Content.DRAFT</tp:dbus-ref> which was removed.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="Contents" type="ao" access="read"
+ tp:name-for-bindings="Contents">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The list of
+ <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call">Content.DRAFT</tp:dbus-ref>
+ objects that are part of this call. Change notification
+ is via the <tp:member-ref>ContentAdded</tp:member-ref> and
+ <tp:member-ref>ContentRemoved</tp:member-ref> signals.
+ </p>
+ </tp:docstring>
+ </property>
+
+ <tp:enum type="u" name="Call_State">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The state of a call, as a whole.</p>
+
+ <p>The allowed transitions are:</p>
+
+ <ul>
+ <li>Pending_Initiator → Pending_Receiver (for outgoing calls,
+ when <tp:member-ref>Accept</tp:member-ref> is called)</li>
+ <li>Pending_Receiver → Accepted (for incoming calls, when
+ <tp:member-ref>Accept</tp:member-ref> is called; for outgoing
+ calls to a contact, when the remote contact accepts the call;
+ for joining a conference call, when the local user successfully
+ joins the conference)</li>
+ <li>Accepted → Pending_Receiver (when transferred to another
+ contact)</li>
+ <li>any state → Ended (when the call is terminated normally, or
+ when an error occurs)</li>
+ </ul>
+
+ <p>Clients MAY consider unknown values from this enum to be an
+ error - additional values will not be defined after the Call
+ specification is declared to be stable.</p>
+ </tp:docstring>
+
+ <tp:enumvalue suffix="Unknown" value = "0">
+ <tp:docstring>
+ The call state is not known. This call state MUST NOT appear as a
+ value of the <tp:member-ref>CallState</tp:member-ref> property, but
+ MAY be used by client code to represent calls whose state is as yet
+ unknown.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Pending_Initiator" value = "1">
+ <tp:docstring>
+ The initiator of the call hasn't accepted the call yet. This state
+ only makes sense for outgoing calls, where it means that the local
+ user has not yet sent any signalling messages to the remote user(s),
+ and will not do so until <tp:member-ref>Accept</tp:member-ref> is
+ called.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Pending_Receiver" value = "2">
+ <tp:docstring>
+ The receiver (the contact being called) hasn't accepted the call yet.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Accepted" value = "3">
+ <tp:docstring>
+ The contact being called has accepted the call.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Ended" value = "4">
+ <tp:docstring>
+ The call has ended, either via normal termination or an error.
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <tp:flags name="Call_Flags" value-prefix="Call_Flag" type="u">
+ <tp:docstring>
+ A set of flags representing the status of the call as a whole,
+ providing more specific information than the
+ <tp:member-ref>CallState</tp:member-ref>. Many of these flags only make
+ sense in a particular state.
+ </tp:docstring>
+
+ <tp:flag suffix="Locally_Ringing" value="1">
+ <tp:docstring>
+ The local contact has been alerted about the call but has not
+ responded; if possible, the remote contact(s) have been informed of
+ this fact. This flag only makes sense on incoming calls in
+ state Call_State_Pending_Receiver. It SHOULD be set when
+ <tp:member-ref>Ringing</tp:member-ref> is called successfully, and
+ unset when the state changes.
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="Queued" value="2">
+ <tp:docstring>
+ The contact is temporarily unavailable, and the call has been placed
+ in a queue (e.g. 182 Queued in SIP, or call-waiting in telephony).
+ This flag only makes sense on outgoing 1-1 calls in
+ state Call_State_Pending_Receiver. It SHOULD be set or unset
+ according to informational messages from other contacts.
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="Locally_Held" value="4">
+ <tp:docstring>
+ The call has been put on hold by the local user, e.g. using the
+ <tp:dbus-ref namespace="org.freedesktop.Telepathy.Channel.Interface"
+ >Hold</tp:dbus-ref> interface. This flag SHOULD only be set if
+ there is at least one Content, and all Contents are locally held;
+ it makes sense on calls in state Call_State_Pending_Receiver or
+ Call_State_Accepted.
+
+ <tp:rationale>
+ Otherwise, in transient situations where some but not all contents
+ are on hold, UIs would falsely indicate that the call as a whole
+ is on hold, which could lead to the user saying something they'll
+ regret, while under the impression that the other contacts can't
+ hear them!
+ </tp:rationale>
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="Forwarded" value="8">
+ <tp:docstring>
+ The initiator of the call originally called a contact other than the
+ current recipient of the call, but the call was then forwarded or
+ diverted. This flag only makes sense on outgoing calls, in state
+ Call_State_Pending_Receiver or Call_State_Accepted. It SHOULD be
+ set or unset according to informational messages from other contacts.
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="In_Progress" value="16">
+ <tp:docstring>
+ Progress has been made in placing the outgoing call, but the
+ contact may not have been made aware of the call yet
+ (so the Ringing state is not appropriate). This corresponds to SIP's
+ status code 183 Session Progress, and could be used when the
+ outgoing call has reached a gateway, for instance.
+ This flag only makes sense on outgoing calls in state
+ Call_State_Pending_Receiver, and SHOULD be set or unset according to
+ informational messages from servers, gateways and other
+ infrastructure.
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="Clearing" value="32">
+ <tp:docstring>
+ This flag only occurs when the CallState is Ended. The call with
+ this flag set has ended, but not all resources corresponding to the
+ call have been freed yet.
+
+ Depending on the protocol there might be some audible feedback while
+ the clearing flag is set.
+
+ <tp:rationale>
+ In calls following the ITU-T Q.931 standard there is a period of
+ time between the call ending and the underlying channel being
+ completely free for re-use.
+ </tp:rationale>
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="Muted" value="64">
+ <tp:docstring>
+ The call has been muted by the local user, e.g. using the
+ <tp:dbus-ref namespace="org.freedesktop.Telepathy.Call.Content.Interface"
+ >Mute.DRAFT</tp:dbus-ref> interface. This flag SHOULD only be set if
+ there is at least one Content, and all Contents are locally muted;
+ it makes sense on calls in state Call_State_Pending_Receiver or
+ Call_State_Accepted.
+ </tp:docstring>
+ </tp:flag>
+ </tp:flags>
+
+ <property name="CallStateDetails"
+ tp:name-for-bindings="Call_State_Details" type="a{sv}" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A map used to provide optional extensible details for the
+ <tp:member-ref>CallState</tp:member-ref>,
+ <tp:member-ref>CallFlags</tp:member-ref> and/or
+ <tp:member-ref>CallStateReason</tp:member-ref>.</p>
+
+ <p>Well-known keys and their corresponding value types include:</p>
+
+ <dl>
+ <dt>hangup-message - s</dt>
+ <dd>An optional human-readable message sent when the call was ended,
+ corresponding to the Message argument to the
+ <tp:member-ref>Hangup</tp:member-ref> method. This is only
+ applicable when the call state is Call_State_Ended.
+ <tp:rationale>
+ XMPP Jingle can send such messages.
+ </tp:rationale>
+ </dd>
+
+ <dt>queue-message - s</dt>
+ <dd>An optional human-readable message sent when the local contact
+ is being held in a queue. This is only applicable when
+ Call_Flag_Queued is in the call flags.
+ <tp:rationale>
+ SIP 182 notifications can have human-readable messages attached.
+ </tp:rationale>
+ </dd>
+
+ <dt>debug-message - s</dt>
+ <dd>A message giving further details of any error indicated by the
+ <tp:member-ref>CallStateReason</tp:member-ref>. This will not
+ normally be localized or suitable for display to users, and is only
+ applicable when the call state is Call_State_Ended.</dd>
+ </dl>
+ </tp:docstring>
+ </property>
+
+ <property name="CallState" type="u" access="read"
+ tp:name-for-bindings="Call_State" tp:type="Call_State">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The current high-level state of this call. The
+ <tp:member-ref>CallFlags</tp:member-ref> provide additional
+ information, and the <tp:member-ref>CallStateReason</tp:member-ref>
+ and <tp:member-ref>CallStateDetails</tp:member-ref> explain the
+ reason for the current values for those properties.</p>
+
+ <p>Clients MAY consider unknown values in this property to be an
+ error.</p>
+ </tp:docstring>
+ </property>
+
+ <property name="CallFlags" type="u" access="read"
+ tp:name-for-bindings="Call_Flags" tp:type="Call_Flags">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Flags representing the status of the call as a whole,
+ providing more specific information than the
+ <tp:member-ref>CallState</tp:member-ref>.</p>
+
+ <p>Clients are expected to ignore unknown flags in this property,
+ without error.</p>
+ </tp:docstring>
+ </property>
+
+ <tp:enum name="Call_State_Change_Reason" type="u">
+ <tp:docstring>
+ A simple representation of the reason for a change in the call's
+ state, which may be used by simple clients, or used as a fallback
+ when the DBus_Reason member of a <tp:type>Call_State_Reason</tp:type>
+ struct is not understood.
+ </tp:docstring>
+
+ <tp:enumvalue suffix="Unknown" value="0">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ We just don't know. Unknown values of this enum SHOULD also be
+ treated like this.
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="User_Requested" value="1">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The change was requested by the contact indicated by the Actor
+ member of a <tp:type>Call_State_Reason</tp:type> struct.</p>
+
+ <p>If the Actor is the local user, the DBus_Reason SHOULD be the
+ empty string.</p>
+
+ <p>If the Actor is a remote user, the DBus_Reason SHOULD be the empty
+ string if the call was terminated normally, but MAY be a non-empty
+ error name to indicate error-like call termination reasons (call
+ rejected as busy, kicked from a conference by a moderator, etc.).</p>
+ </tp:docstring>
+ </tp:enumvalue>
+
+ <tp:enumvalue suffix="Forwarded" value="2">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The call was forwarded. If known, the handle of the contact
+ the call was forwarded to will be indicated by the Actor member
+ of a <tp:type>Call_State_Reason</tp:type> struct.</p>
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <tp:struct name="Call_State_Reason">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A description of the reason for a change to the
+ <tp:member-ref>CallState</tp:member-ref> and/or
+ <tp:member-ref>CallFlags</tp:member-ref>.</p>
+ </tp:docstring>
+
+ <tp:member type="u" tp:type="Contact_Handle" name="Actor">
+ <tp:docstring>
+ The contact responsible for the change, or 0 if no contact was
+ responsible.
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member type="u" tp:type="Call_State_Change_Reason" name="Reason">
+ <tp:docstring>
+ The reason, chosen from a limited set of possibilities defined by
+ the Telepathy specification.
+ </tp:docstring>
+ </tp:member>
+
+ <tp:member type="s" tp:type="DBus_Error_Name" name="DBus_Reason">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A specific reason for the change, which may be a D-Bus error in
+ the Telepathy namespace, a D-Bus error in any other namespace
+ (for implementation-specific errors), or the empty string to
+ indicate that the state change was not an error.</p>
+
+ <p>This SHOULD be an empty string for changes to any state other
+ than Ended.</p>
+
+ <p>The errors Cancelled and Terminated SHOULD NOT be used here;
+ an empty string SHOULD be used instead.</p>
+
+ <tp:rationale>
+ <p>Those error names are used to indicate normal call
+ termination by the local user or another user, respectively,
+ in contexts where a D-Bus error name must appear.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <property name="CallStateReason" tp:name-for-bindings="Call_State_Reason"
+ type="(uus)" access="read" tp:type="Call_State_Reason">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The reason for the last change to the
+ <tp:member-ref>CallState</tp:member-ref> and/or
+ <tp:member-ref>CallFlags</tp:member-ref>. The
+ <tp:member-ref>CallStateDetails</tp:member-ref> MAY provide additional
+ information.</p>
+ </tp:docstring>
+ </property>
+
+ <signal name="CallStateChanged"
+ tp:name-for-bindings="Call_State_Changed">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>Emitted when the state of the call as a whole changes.</p>
+
+ <p>This signal is emitted for any change in the properties
+ corresponding to its arguments, even if the other properties
+ referenced remain unchanged.</p>
+ </tp:docstring>
+
+ <arg name="Call_State" type="u" tp:type="Call_State">
+ <tp:docstring>
+ The new value of the <tp:member-ref>CallState</tp:member-ref>
+ property.
+ </tp:docstring>
+ </arg>
+
+ <arg name="Call_Flags" type="u" tp:type="Call_Flags">
+ <tp:docstring>
+ The new value of the <tp:member-ref>CallFlags</tp:member-ref>
+ property.
+ </tp:docstring>
+ </arg>
+
+ <arg name="Call_State_Reason" type="(uus)" tp:type="Call_State_Reason">
+ <tp:docstring>
+ The new value of the <tp:member-ref>CallStateReason</tp:member-ref>
+ property.
+ </tp:docstring>
+ </arg>
+
+ <arg name="Call_State_Details" type="a{sv}">
+ <tp:docstring>
+ The new value of the <tp:member-ref>CallStateDetails</tp:member-ref>
+ property.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="HardwareStreaming" tp:name-for-bindings="Hardware_Streaming"
+ type="b" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>If this property is TRUE, all of the media streaming is done by some
+ mechanism outside the scope of Telepathy.</p>
+
+ <tp:rationale>
+ <p>A connection manager might be intended for a specialized hardware
+ device, which will take care of the audio streaming (e.g.
+ telepathy-yafono, which uses GSM hardware which does the actual
+ audio streaming for the call).</p>
+ </tp:rationale>
+
+ <p>If this is FALSE, the handler is responsible for doing the actual
+ media streaming for at least some contents itself. Those contents
+ will have the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Content.Interface"
+ >Media.DRAFT</tp:dbus-ref> interface, to communicate the necessary
+ information to a streaming implementation. Connection managers SHOULD
+ operate like this, if possible.</p>
+
+ <tp:rationale>
+ <p>Many connection managers (such as telepathy-gabble) only do the
+ call signalling, and expect the client to do the actual streaming
+ using something like
+ <a href="http://farsight.freedesktop.org/">Farsight</a>, to improve
+ latency and allow better UI integration.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </property>
+
+ <tp:flags type="u" name="Call_Member_Flags" value-prefix="Call_Member_Flag">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>A set of flags representing the status of a remote contact in a
+ call.</p>
+
+ <p>It is protocol- and client-specific whether a particular contact
+ will ever have a particular flag set on them, and Telepathy clients
+ SHOULD NOT assume that a flag will ever be set.</p>
+
+ <tp:rationale>
+ <p>180 Ringing in SIP, and its equivalent in XMPP, are optional
+ informational messages, and implementations are not required
+ to send them. The same applies to the messages used to indicate
+ hold state.</p>
+ </tp:rationale>
+ </tp:docstring>
+
+ <tp:flag suffix="Ringing" value = "1">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The remote contact's client has told us that the contact has been
+ alerted about the call but has not responded.</p>
+
+ <tp:rationale>
+ <p>This is a flag per member, not a flag for the call as a whole,
+ because in Muji conference calls, you could invite someone and
+ have their state be "ringing" for a while.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </tp:flag>
+
+ <tp:flag suffix="Held" value = "2">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The call member has put this call on hold.</p>
+
+ <tp:rationale>
+ <p>This is a flag per member, not a flag for the call as a whole,
+ because in conference calls, any member could put the conference
+ on hold.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </tp:flag>
+ </tp:flags>
+
+ <tp:mapping name="Call_Member_Map" array-name="Call_Member_Map_List">
+ <tp:docstring>A mapping from handles to their current state in the call.
+ </tp:docstring>
+ <tp:member type="u" tp:type="Handle" name="key"/>
+ <tp:member type="u" tp:type="Call_Member_Flags" name="Flag"/>
+ </tp:mapping>
+
+ <signal name="CallMembersChanged"
+ tp:name-for-bindings="Call_Members_Changed">
+ <tp:docstring>
+ Emitted when the <tp:member-ref>CallMembers</tp:member-ref> property
+ changes in any way, either because contacts have been added to the
+ call, contacts have been removed from the call, or contacts' flags
+ have changed.
+ </tp:docstring>
+
+ <arg name="Flags_Changed" type="a{uu}" tp:type="Call_Member_Map">
+ <tp:docstring>
+ A map from members of the call to their new call member flags,
+ including at least the members who have been added to
+ <tp:member-ref>CallMembers</tp:member-ref>, and the members whose
+ flags have changed.
+ </tp:docstring>
+ </arg>
+ <arg name="Removed" type="au" tp:type="Contact_Handle[]">
+ <tp:docstring>
+ A list of members who have left the call, i.e. keys to be removed
+ from <tp:member-ref>CallMembers</tp:member-ref>.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <property name="CallMembers" tp:name-for-bindings="Call_Members"
+ type="a{uu}" access="read" tp:type="Call_Member_Map">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ A mapping from the remote contacts that are part of this call to flags
+ discribing their status. This mapping never has the local user's handle
+ as a key.
+ </tp:docstring>
+ </property>
+
+ <property name="InitialTransport" tp:name-for-bindings="Initial_Transport"
+ type="s" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ If set on a requested channel this indicates the transport that
+ should be used for this call.
+ <tp:rationale>
+ When implementing a voip gateway one wants the outgoing leg of the
+ gatewayed to have the same transport as the incoming leg. This
+ property allows the gateway to request a Call with the right
+ transport from the CM.
+ </tp:rationale>
+ </p>
+ </tp:docstring>
+ </property>
+
+ <property name="InitialAudio" tp:name-for-bindings="Initial_Audio"
+ type="b" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>If set to true in a channel request that will create a new channel,
+ the connection manager should immediately attempt to establish an
+ audio stream to the remote contact, making it unnecessary for the
+ client to call
+ <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Type.Call.DRAFT">AddContent</tp:dbus-ref>.
+ </p>
+
+ <p>If this property, or InitialVideo, is passed to EnsureChannel
+ (as opposed to CreateChannel), the connection manager SHOULD ignore
+ these properties when checking whether it can return an existing
+ channel as suitable; these properties only become significant when
+ the connection manager has decided to create a new channel.</p>
+
+ <p>If true on a requested channel, this indicates that the audio
+ stream has already been requested and the client does not need to
+ call RequestStreams, although it MAY still do so.</p>
+
+ <p>If true on an unrequested (incoming) channel, this indicates that
+ the remote contact initially requested an audio stream; this does
+ not imply that that audio stream is still active (as indicated by
+ <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Type.Call.DRAFT">Contents</tp:dbus-ref>).</p>
+
+ <p>This property is immutable (cannot change), and therefore SHOULD
+ appear wherever immutable properties are reported, e.g. <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Connection.Interface.Requests">NewChannels</tp:dbus-ref>
+ signals.</p>
+
+ <tp:rationale><p>This reduces D-Bus round trips.</p></tp:rationale>
+
+ <p>Connection managers that support the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Connection.Interface">ContactCapabilities</tp:dbus-ref>
+ interface SHOULD represent the capabilities of receiving audio
+ and/or video calls by including a channel class in
+ a contact's capabilities with ChannelType = Call
+ in the fixed properties dictionary, and InitialAudio and/or
+ InitialVideo in the allowed properties list. Clients wishing to
+ discover whether a particular contact is likely to be able to
+ receive audio and/or video calls SHOULD use this information.</p>
+
+ <tp:rationale>
+ <p>Not all clients support video calls, and it would also be
+ possible (although unlikely) to have a client which could only
+ stream video, not audio.</p>
+ </tp:rationale>
+
+ <p>Clients that are willing to receive audio and/or video calls
+ SHOULD include the following among their channel classes if
+ calling <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities">UpdateCapabilities</tp:dbus-ref>
+ (clients of a <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy">ChannelDispatcher</tp:dbus-ref>
+ SHOULD instead arrange for the ChannelDispatcher to do this,
+ by including the filters in their <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Client.Handler">HandlerChannelFilter</tp:dbus-ref>
+ properties):</p>
+
+ <ul>
+ <li>{ ChannelType = Call }</li>
+ <li>{ ChannelType = Call, InitialAudio = true }
+ if receiving calls with audio is supported</li>
+ <li>{ ChannelType = Call, InitialVideo = true }
+ if receiving calls with video is supported</li>
+ </ul>
+
+ <tp:rationale>
+ <p>Connection managers for protocols with capability discovery,
+ like XMPP, need this information to advertise the appropriate
+ capabilities for their protocol.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </property>
+
+ <property name="InitialVideo" tp:name-for-bindings="Initial_Video"
+ type="b" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The same as <tp:member-ref>InitialAudio</tp:member-ref>, but for
+ a video stream. This property is immutable (cannot change).</p>
+
+ <p>In particular, note that if this property is false, this does not
+ imply that an active video stream has not been added, only that no
+ video stream was active at the time the channel appeared.</p>
+
+ <p>This property is the correct way to discover whether connection
+ managers, contacts etc. support video calls; it appears in
+ capabilities structures in the same way as InitialAudio.</p>
+ </tp:docstring>
+ </property>
+
+ <property name="MutableContents" tp:name-for-bindings="Mutable_Contents"
+ type="b" access="read">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>If <tt>True</tt>, a stream of a different content type can be added
+ after the Channel has been requested </p>
+
+ <p>If this property is missing, clients SHOULD assume that it is false,
+ and thus that the channel's streams cannot be changed once the call
+ has started.</p>
+
+ <p>If this property isn't present in the "allowed" set in any of the
+ Call entries contact capabilities, then user interfaces MAY choose to
+ show a separate "call" option for each class of call.</p>
+
+ <tp:rationale>
+ <p>For example, once an audio-only Google Talk call has started,
+ it is not possible to add a video stream; both audio and video
+ must be requested at the start of the call if video is desired.
+ User interfaces may use this pseudo-capability as a hint to
+ display separate "Audio call" and "Video call" buttons, rather
+ than a single "Call" button with the option to add and remove
+ video once the call has started for contacts without this flag.
+ </p>
+ </tp:rationale>
+
+ <p>This property is immutable, and therefore SHOULD be announced
+ in <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Connection.Interface.Requests">NewChannels</tp:dbus-ref>,
+ etc.</p>
+ </tp:docstring>
+ </property>
+
+ <tp:hct name="audio">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>This client supports audio calls.</p>
+ </tp:docstring>
+ </tp:hct>
+
+ <tp:hct name="video">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>This client supports video calls.</p>
+ </tp:docstring>
+ </tp:hct>
+
+ <tp:hct name="gtalk-p2p">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The client can implement streaming for streams whose <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref>
+ property is Stream_Transport_Type_GTalk_P2P.</p>
+ </tp:docstring>
+ </tp:hct>
+
+ <tp:hct name="ice">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The client can implement streaming for streams whose <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref>
+ property is Stream_Transport_Type_ICE.</p>
+ </tp:docstring>
+ </tp:hct>
+
+ <tp:hct name="wlm-8.5">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The client can implement streaming for streams whose <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref>
+ property is Stream_Transport_Type_WLM_8_5.</p>
+ </tp:docstring>
+ </tp:hct>
+
+ <tp:hct name="wlm-2009">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The client can implement streaming for streams whose <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref>
+ property is Stream_Transport_Type_WLM_2009.</p>
+ </tp:docstring>
+ </tp:hct>
+
+ <tp:hct name="video/h264" is-family="yes">
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>The client supports media streaming with H264 (etc.).</p>
+
+ <p>This handler capability token is a one of a family
+ of similar tokens: for any other audio or video codec whose MIME
+ type is audio/<em>subtype</em> or video/<em>subtype</em>, a handler
+ capability token of this form may exist (the subtype MUST appear
+ in lower case in this context). Clients MAY support more
+ codecs than they explicitly advertise support for; clients SHOULD
+ explicitly advertise support for their preferred codec(s), and
+ for codecs like H264 that are, in practice, significant in codec
+ negotiation.</p>
+
+ <tp:rationale>
+ <p>For instance, the XMPP capability used by the Google Video
+ Chat web client to determine whether a client is compatible
+ with it requires support for H264 video, so an XMPP
+ connection manager that supports this version of Jingle should
+ not advertise the Google Video Chat capability unless there
+ is at least one installed client that declares that it supports
+ <code>video/h264</code> on Call channels.</p>
+ </tp:rationale>
+
+ <p>For example, a client could advertise support for audio and video
+ calls using Speex, Theora and H264 by having five handler capability
+ tokens in its <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Client.Handler">Capabilities</tp:dbus-ref>
+ property:</p>
+
+ <ul>
+ <li><code>org.freedesktop.Telepathy.Channel.Type.Call.DRAFT/audio</code></li>
+ <li><code>org.freedesktop.Telepathy.Channel.Type.Call.DRAFT/audio/speex</code></li>
+ <li><code>org.freedesktop.Telepathy.Channel.Type.Call.DRAFT/video</code></li>
+ <li><code>org.freedesktop.Telepathy.Channel.Type.Call.DRAFT/video/theora</code></li>
+ <li><code>org.freedesktop.Telepathy.Channel.Type.Call.DRAFT/video/h264</code></li>
+ </ul>
+
+ <p>Clients MAY have media signalling abilities without explicitly
+ supporting any particular codec, and connection managers SHOULD
+ support this usage.</p>
+
+ <tp:rationale>
+ <p>This is necessary to support gatewaying between two Telepathy
+ connections, in which case the available codecs might not be
+ known to the gatewaying process.</p>
+ </tp:rationale>
+ </tp:docstring>
+ </tp:hct>
+
+ </interface>
+</node>
+<!-- vim:set sw=2 sts=2 et ft=xml: -->
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
new file mode 100644
index 0000000..3e3d1ad
--- /dev/null
+++ b/extensions/Makefile.am
@@ -0,0 +1,174 @@
+# This directory re-uses telepathy-glib's code generation mechanisms to
+# generate code for interfaces that aren't stable enough for telepathy-glib
+# yet, so we can start to adapt example code to use them.
+
+tools_dir = $(top_srcdir)/tools
+
+AM_CFLAGS = \
+ $(ERROR_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(TELEPATHY_CFLAGS)
+
+EXTRA_DIST = \
+ all.xml \
+ call-content.xml \
+ call-stream.xml \
+ channel.xml \
+ misc.xml
+
+noinst_LTLIBRARIES = libfuture-extensions.la
+
+# In an external project you'd use $(TP_GLIB_LIBS) (obtained from
+# pkg-config via autoconf) instead of the .la path
+libfuture_extensions_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(TELEPATHY_LIBS)
+
+libfuture_extensions_la_SOURCES = \
+ call-content.c \
+ call-content.h \
+ call-stream.c \
+ call-stream.h \
+ extensions.c \
+ extensions-cli.c \
+ extensions.h
+
+nodist_libfuture_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-call-content.h \
+ _gen/cli-call-content-body.h \
+ _gen/cli-call-stream.h \
+ _gen/cli-call-stream-body.h \
+ _gen/cli-channel.h \
+ _gen/cli-channel-body.h \
+ _gen/cli-misc.h \
+ _gen/cli-misc-body.h \
+ _gen/svc-call-content.h \
+ _gen/svc-call-content.c \
+ _gen/svc-call-stream.h \
+ _gen/svc-call-stream.c \
+ _gen/svc-channel.h \
+ _gen/svc-channel.c \
+ _gen/svc-misc.h \
+ _gen/svc-misc.c
+
+BUILT_SOURCES = \
+ _gen/all.xml \
+ _gen/call-content.xml \
+ _gen/call-stream.xml \
+ _gen/channel.xml \
+ _gen/misc.xml \
+ $(nodist_libfuture_extensions_la_SOURCES)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+clean-local:
+ rm -rf _gen
+
+XSLTPROCFLAGS = --nonet --novalid
+
+# Generated files which can be generated for all categories simultaneously
+
+_gen/all.xml: all.xml $(wildcard $(srcdir)/*.xml) $(wildcard $(top_srcdir)/spec/*.xml) $(tools_dir)/xincludator.py
+ $(mkdir_p) _gen
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@
+
+_gen/gtypes.h _gen/gtypes-body.h: _gen/all.xml \
+ $(top_srcdir)/tools/glib-gtypes-generator.py
+ $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/glib-gtypes-generator.py \
+ $< _gen/gtypes Future
+
+_gen/signals-marshal.list: _gen/all.xml \
+ $(tools_dir)/glib-signals-marshal-gen.py
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-signals-marshal-gen.py $< > $@
+
+_gen/signals-marshal.h: _gen/signals-marshal.list Makefile.am
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) --header --prefix=_future_ext_marshal $< > $@
+
+_gen/signals-marshal.c: _gen/signals-marshal.list Makefile.am
+ $(AM_V_GEN){ echo '#include "_gen/signals-marshal.h"' && \
+ $(GLIB_GENMARSHAL) --body --prefix=_future_ext_marshal $< ; } > $@
+
+_gen/register-dbus-glib-marshallers-body.h: _gen/all.xml \
+ $(tools_dir)/glib-client-marshaller-gen.py
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \
+ _future_ext > $@
+
+_gen/enums.h: _gen/all.xml \
+ $(tools_dir)/c-constants-gen.py
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py \
+ Future \
+ $< _gen/enums
+
+_gen/interfaces-body.h _gen/interfaces.h: _gen/all.xml \
+ $(tools_dir)/glib-interfaces-gen.py
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \
+ Future _gen/interfaces-body.h _gen/interfaces.h $<
+
+# 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/%.xml: %.xml $(wildcard $(srcdir)/*.xml) $(wildcard $(top_srcdir)/spec/*.xml) $(tools_dir)/xincludator.py
+ $(mkdir_p) _gen
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/xincludator.py $< > $@
+
+_gen/svc-%.c _gen/svc-%.h: _gen/%.xml \
+ $(tools_dir)/glib-ginterface-gen.py
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \
+ --filename=_gen/svc-$* \
+ --signal-marshal-prefix=_future_ext \
+ --include='<telepathy-glib/dbus.h>' \
+ --include='"_gen/signals-marshal.h"' \
+ --not-implemented-func='tp_dbus_g_method_return_not_implemented' \
+ --allow-unstable \
+ $< Future_Svc_
+
+_gen/cli-channel-body.h _gen/cli-channel.h: _gen/channel.xml \
+ $(tools_dir)/glib-client-gen.py Makefile.am
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \
+ --group=channel \
+ --subclass=TpChannel \
+ --subclass-assert=TP_IS_CHANNEL \
+ --iface-quark-prefix=FUTURE_IFACE_QUARK \
+ --tp-proxy-api=0.7.6 \
+ $< Future_Cli _gen/cli-channel
+
+_gen/cli-call-content-body.h _gen/cli-call-content.h: _gen/call-content.xml \
+ $(tools_dir)/glib-client-gen.py Makefile.am
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \
+ --group=call_content \
+ --subclass=FutureCallContent \
+ --subclass-assert=FUTURE_IS_CALL_CONTENT \
+ --iface-quark-prefix=FUTURE_IFACE_QUARK \
+ --tp-proxy-api=0.7.6 \
+ $< Future_Cli _gen/cli-call-content
+
+_gen/cli-call-stream-body.h _gen/cli-call-stream.h: _gen/call-stream.xml \
+ $(tools_dir)/glib-client-gen.py Makefile.am
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \
+ --group=call_stream \
+ --subclass=FutureCallStream \
+ --subclass-assert=FUTURE_IS_CALL_STREAM \
+ --iface-quark-prefix=FUTURE_IFACE_QUARK \
+ --tp-proxy-api=0.7.6 \
+ $< Future_Cli _gen/cli-call-stream
+
+# for now the Endpoint etc. interfaces are on every TpProxy - when we
+# have a TpCallEndpoint etc., they should appear on that
+
+_gen/cli-misc-body.h _gen/cli-misc.h: _gen/misc.xml \
+ $(tools_dir)/glib-client-gen.py Makefile.am
+ $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-gen.py \
+ --group=misc \
+ --iface-quark-prefix=FUTURE_IFACE_QUARK \
+ --tp-proxy-api=0.7.6 \
+ $< Future_Cli _gen/cli-misc
diff --git a/extensions/all.xml b/extensions/all.xml
new file mode 100644
index 0000000..618cef7
--- /dev/null
+++ b/extensions/all.xml
@@ -0,0 +1,12 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Extensions from the future</tp:title>
+
+<xi:include href="call-content.xml"/>
+<xi:include href="call-stream.xml"/>
+<xi:include href="channel.xml"/>
+<xi:include href="misc.xml"/>
+
+</tp:spec>
diff --git a/extensions/call-content.c b/extensions/call-content.c
new file mode 100644
index 0000000..29d10ce
--- /dev/null
+++ b/extensions/call-content.c
@@ -0,0 +1,161 @@
+/*
+ * call-content.c - proxy for a Content in a Call channel
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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
+ */
+
+#include "extensions/call-content.h"
+
+#include <telepathy-glib/proxy-subclass.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "extensions/extensions.h"
+
+/* Generated code */
+#include "_gen/cli-call-content-body.h"
+
+/**
+ * SECTION:call-content
+ * @title: FutureCallContent
+ * @short_description: proxy for a Content in a Call channel
+ * @see_also: #TpChannel
+ *
+ * FIXME
+ *
+ * Since: FIXME
+ */
+
+/**
+ * FutureCallContentClass:
+ *
+ * The class of a #FutureCallContent.
+ *
+ * Since: FIXME
+ */
+struct _FutureCallContentClass {
+ TpProxyClass parent_class;
+ /*<private>*/
+ gpointer priv;
+};
+
+/**
+ * FutureCallContent:
+ *
+ * A proxy object for a Telepathy connection manager.
+ *
+ * Since: FIXME
+ */
+struct _FutureCallContent {
+ TpProxy parent;
+ /*<private>*/
+ FutureCallContentPrivate *priv;
+};
+
+struct _FutureCallContentPrivate {
+ int dummy;
+};
+
+G_DEFINE_TYPE (FutureCallContent,
+ future_call_content,
+ TP_TYPE_PROXY);
+
+static void
+future_call_content_init (FutureCallContent *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FUTURE_TYPE_CALL_CONTENT,
+ FutureCallContentPrivate);
+}
+
+static void
+future_call_content_class_init (FutureCallContentClass *klass)
+{
+ TpProxyClass *proxy_class = (TpProxyClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (FutureCallContentPrivate));
+
+ proxy_class->must_have_unique_name = TRUE;
+ proxy_class->interface = FUTURE_IFACE_QUARK_CALL_CONTENT;
+ future_call_content_init_known_interfaces ();
+}
+
+/**
+ * future_call_content_new:
+ * @channel: the Call channel
+ * @object_path: the object path of the content; may not be %NULL
+ * @error: used to indicate the error if %NULL is returned
+ *
+ * <!-- -->
+ *
+ * Returns: a new content proxy, or %NULL on invalid arguments
+ *
+ * Since: FIXME
+ */
+FutureCallContent *
+future_call_content_new (TpChannel *channel,
+ const gchar *object_path,
+ GError **error)
+{
+ FutureCallContent *ret = NULL;
+
+ g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
+ g_return_val_if_fail (object_path != NULL, NULL);
+
+ if (!tp_dbus_check_valid_object_path (object_path, error))
+ goto finally;
+
+ ret = FUTURE_CALL_CONTENT (g_object_new (FUTURE_TYPE_CALL_CONTENT,
+ /* FIXME: pass in the Channel as a property? */
+ "dbus-daemon", tp_proxy_get_dbus_daemon (channel),
+ "bus-name", tp_proxy_get_bus_name (channel),
+ "object-path", object_path,
+ NULL));
+
+finally:
+ return ret;
+}
+
+/**
+ * future_call_content_init_known_interfaces:
+ *
+ * Ensure that the known interfaces for FutureCallContent have been set up.
+ * This is done automatically when necessary, but for correct
+ * overriding of library interfaces by local extensions, you should
+ * call this function before calling
+ * tp_proxy_or_subclass_hook_on_interface_add() with first argument
+ * %FUTURE_TYPE_CALL_CONTENT.
+ *
+ * Since: 0.7.32
+ */
+void
+future_call_content_init_known_interfaces (void)
+{
+ static gsize once = 0;
+
+ if (g_once_init_enter (&once))
+ {
+ GType tp_type = FUTURE_TYPE_CALL_CONTENT;
+
+ tp_proxy_init_known_interfaces ();
+ tp_proxy_or_subclass_hook_on_interface_add (tp_type,
+ future_cli_call_content_add_signals);
+ tp_proxy_subclass_add_error_mapping (tp_type,
+ TP_ERROR_PREFIX, TP_ERRORS, TP_TYPE_ERROR);
+
+ g_once_init_leave (&once, 1);
+ }
+}
diff --git a/extensions/call-content.h b/extensions/call-content.h
new file mode 100644
index 0000000..5f674f2
--- /dev/null
+++ b/extensions/call-content.h
@@ -0,0 +1,62 @@
+/*
+ * call-content.h - proxy for a Content in a Call channel
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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
+ */
+
+#ifndef FUTURE_CALL_CONTENT_H
+#define FUTURE_CALL_CONTENT_H
+
+#include <telepathy-glib/channel.h>
+#include <telepathy-glib/proxy.h>
+
+G_BEGIN_DECLS
+
+typedef struct _FutureCallContent FutureCallContent;
+typedef struct _FutureCallContentPrivate FutureCallContentPrivate;
+typedef struct _FutureCallContentClass FutureCallContentClass;
+
+GType future_call_content_get_type (void);
+
+/* TYPE MACROS */
+#define FUTURE_TYPE_CALL_CONTENT \
+ (future_call_content_get_type ())
+#define FUTURE_CALL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), FUTURE_TYPE_CALL_CONTENT, \
+ FutureCallContent))
+#define FUTURE_CALL_CONTENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), FUTURE_TYPE_CALL_CONTENT, \
+ FutureCallContentClass))
+#define FUTURE_IS_CALL_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), FUTURE_TYPE_CALL_CONTENT))
+#define FUTURE_IS_CALL_CONTENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), FUTURE_TYPE_CALL_CONTENT))
+#define FUTURE_CALL_CONTENT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), FUTURE_TYPE_CALL_CONTENT, \
+ FutureCallContentClass))
+
+FutureCallContent *future_call_content_new (TpChannel *channel,
+ const gchar *object_path, GError **error);
+
+void future_call_content_init_known_interfaces (void);
+
+G_END_DECLS
+
+#include "extensions/_gen/cli-call-content.h"
+
+#endif
diff --git a/extensions/call-content.xml b/extensions/call-content.xml
new file mode 100644
index 0000000..cfb1000
--- /dev/null
+++ b/extensions/call-content.xml
@@ -0,0 +1,10 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Call Content</tp:title>
+
+<xi:include href="Call_Content.xml"/>
+<xi:include href="Call_Content_Interface_Media.xml"/>
+
+</tp:spec>
diff --git a/extensions/call-stream.c b/extensions/call-stream.c
new file mode 100644
index 0000000..b995398
--- /dev/null
+++ b/extensions/call-stream.c
@@ -0,0 +1,161 @@
+/*
+ * call-stream.c - proxy for a Stream in a Call channel
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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
+ */
+
+#include "extensions/call-stream.h"
+
+#include <telepathy-glib/proxy-subclass.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "extensions/extensions.h"
+
+/* Generated code */
+#include "_gen/cli-call-stream-body.h"
+
+/**
+ * SECTION:call-stream
+ * @title: FutureCallStream
+ * @short_description: proxy for a Stream in a Call channel
+ * @see_also: #TpChannel
+ *
+ * FIXME
+ *
+ * Since: FIXME
+ */
+
+/**
+ * FutureCallStreamClass:
+ *
+ * The class of a #FutureCallStream.
+ *
+ * Since: FIXME
+ */
+struct _FutureCallStreamClass {
+ TpProxyClass parent_class;
+ /*<private>*/
+ gpointer priv;
+};
+
+/**
+ * FutureCallStream:
+ *
+ * A proxy object for a Telepathy connection manager.
+ *
+ * Since: FIXME
+ */
+struct _FutureCallStream {
+ TpProxy parent;
+ /*<private>*/
+ FutureCallStreamPrivate *priv;
+};
+
+struct _FutureCallStreamPrivate {
+ int dummy;
+};
+
+G_DEFINE_TYPE (FutureCallStream,
+ future_call_stream,
+ TP_TYPE_PROXY);
+
+static void
+future_call_stream_init (FutureCallStream *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FUTURE_TYPE_CALL_STREAM,
+ FutureCallStreamPrivate);
+}
+
+static void
+future_call_stream_class_init (FutureCallStreamClass *klass)
+{
+ TpProxyClass *proxy_class = (TpProxyClass *) klass;
+
+ g_type_class_add_private (klass, sizeof (FutureCallStreamPrivate));
+
+ proxy_class->must_have_unique_name = TRUE;
+ proxy_class->interface = FUTURE_IFACE_QUARK_CALL_STREAM;
+ future_call_stream_init_known_interfaces ();
+}
+
+/**
+ * future_call_stream_new:
+ * @channel: the Call channel
+ * @object_path: the object path of the stream; may not be %NULL
+ * @error: used to indicate the error if %NULL is returned
+ *
+ * <!-- -->
+ *
+ * Returns: a new stream proxy, or %NULL on invalid arguments
+ *
+ * Since: FIXME
+ */
+FutureCallStream *
+future_call_stream_new (TpChannel *channel,
+ const gchar *object_path,
+ GError **error)
+{
+ FutureCallStream *ret = NULL;
+
+ g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
+ g_return_val_if_fail (object_path != NULL, NULL);
+
+ if (!tp_dbus_check_valid_object_path (object_path, error))
+ goto finally;
+
+ ret = FUTURE_CALL_STREAM (g_object_new (FUTURE_TYPE_CALL_STREAM,
+ /* FIXME: pass in the Channel as a property? */
+ "dbus-daemon", tp_proxy_get_dbus_daemon (channel),
+ "bus-name", tp_proxy_get_bus_name (channel),
+ "object-path", object_path,
+ NULL));
+
+finally:
+ return ret;
+}
+
+/**
+ * future_call_stream_init_known_interfaces:
+ *
+ * Ensure that the known interfaces for FutureCallStream have been set up.
+ * This is done automatically when necessary, but for correct
+ * overriding of library interfaces by local extensions, you should
+ * call this function before calling
+ * tp_proxy_or_subclass_hook_on_interface_add() with first argument
+ * %FUTURE_TYPE_CALL_STREAM.
+ *
+ * Since: 0.7.32
+ */
+void
+future_call_stream_init_known_interfaces (void)
+{
+ static gsize once = 0;
+
+ if (g_once_init_enter (&once))
+ {
+ GType tp_type = FUTURE_TYPE_CALL_STREAM;
+
+ tp_proxy_init_known_interfaces ();
+ tp_proxy_or_subclass_hook_on_interface_add (tp_type,
+ future_cli_call_stream_add_signals);
+ tp_proxy_subclass_add_error_mapping (tp_type,
+ TP_ERROR_PREFIX, TP_ERRORS, TP_TYPE_ERROR);
+
+ g_once_init_leave (&once, 1);
+ }
+}
diff --git a/extensions/call-stream.h b/extensions/call-stream.h
new file mode 100644
index 0000000..0a195bd
--- /dev/null
+++ b/extensions/call-stream.h
@@ -0,0 +1,62 @@
+/*
+ * call-stream.h - proxy for a Stream in a Call channel
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * 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
+ */
+
+#ifndef FUTURE_CALL_STREAM_H
+#define FUTURE_CALL_STREAM_H
+
+#include <telepathy-glib/channel.h>
+#include <telepathy-glib/proxy.h>
+
+G_BEGIN_DECLS
+
+typedef struct _FutureCallStream FutureCallStream;
+typedef struct _FutureCallStreamPrivate FutureCallStreamPrivate;
+typedef struct _FutureCallStreamClass FutureCallStreamClass;
+
+GType future_call_stream_get_type (void);
+
+/* TYPE MACROS */
+#define FUTURE_TYPE_CALL_STREAM \
+ (future_call_stream_get_type ())
+#define FUTURE_CALL_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), FUTURE_TYPE_CALL_STREAM, \
+ FutureCallStream))
+#define FUTURE_CALL_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), FUTURE_TYPE_CALL_STREAM, \
+ FutureCallStreamClass))
+#define FUTURE_IS_CALL_STREAM(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), FUTURE_TYPE_CALL_STREAM))
+#define FUTURE_IS_CALL_STREAM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), FUTURE_TYPE_CALL_STREAM))
+#define FUTURE_CALL_STREAM_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), FUTURE_TYPE_CALL_STREAM, \
+ FutureCallStreamClass))
+
+FutureCallStream *future_call_stream_new (TpChannel *channel,
+ const gchar *object_path, GError **error);
+
+void future_call_stream_init_known_interfaces (void);
+
+G_END_DECLS
+
+#include "extensions/_gen/cli-call-stream.h"
+
+#endif
diff --git a/extensions/call-stream.xml b/extensions/call-stream.xml
new file mode 100644
index 0000000..3e85b37
--- /dev/null
+++ b/extensions/call-stream.xml
@@ -0,0 +1,10 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Call Stream</tp:title>
+
+<xi:include href="Call_Stream.xml"/>
+<xi:include href="Call_Stream_Interface_Media.xml"/>
+
+</tp:spec>
diff --git a/extensions/channel.xml b/extensions/channel.xml
new file mode 100644
index 0000000..b4e0a46
--- /dev/null
+++ b/extensions/channel.xml
@@ -0,0 +1,9 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Channel extensions from the future</tp:title>
+
+<xi:include href="Channel_Type_Call.xml"/>
+
+</tp:spec>
diff --git a/extensions/extensions-cli.c b/extensions/extensions-cli.c
new file mode 100644
index 0000000..d29c5ec
--- /dev/null
+++ b/extensions/extensions-cli.c
@@ -0,0 +1,35 @@
+#include "extensions.h"
+
+#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/proxy-subclass.h>
+
+static void _future_ext_register_dbus_glib_marshallers (void);
+
+/* include auto-generated stubs for client-specific code */
+#include "_gen/signals-marshal.h"
+#include "_gen/cli-channel-body.h"
+#include "_gen/cli-misc-body.h"
+#include "_gen/register-dbus-glib-marshallers-body.h"
+
+static gpointer
+future_cli_once (gpointer data)
+{
+ _future_ext_register_dbus_glib_marshallers ();
+
+ tp_channel_init_known_interfaces ();
+
+ tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_PROXY,
+ future_cli_misc_add_signals);
+ tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_CHANNEL,
+ future_cli_channel_add_signals);
+
+ return NULL;
+}
+
+void
+future_cli_init (void)
+{
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, future_cli_once, NULL);
+}
diff --git a/extensions/extensions.c b/extensions/extensions.c
new file mode 100644
index 0000000..eeda462
--- /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 0000000..24d9787
--- /dev/null
+++ b/extensions/extensions.h
@@ -0,0 +1,27 @@
+#ifndef FUTURE_EXTENSIONS_H
+#define FUTURE_EXTENSIONS_H
+
+#include <glib-object.h>
+#include <telepathy-glib/telepathy-glib.h>
+
+#include "extensions/_gen/enums.h"
+#include "extensions/_gen/cli-channel.h"
+#include "extensions/_gen/cli-misc.h"
+#include "extensions/_gen/svc-call-content.h"
+#include "extensions/_gen/svc-call-stream.h"
+#include "extensions/_gen/svc-channel.h"
+#include "extensions/_gen/svc-misc.h"
+
+#include "extensions/call-content.h"
+#include "extensions/call-stream.h"
+
+G_BEGIN_DECLS
+
+#include "extensions/_gen/gtypes.h"
+#include "extensions/_gen/interfaces.h"
+
+void future_cli_init (void);
+
+G_END_DECLS
+
+#endif
diff --git a/extensions/misc.xml b/extensions/misc.xml
new file mode 100644
index 0000000..0384410
--- /dev/null
+++ b/extensions/misc.xml
@@ -0,0 +1,10 @@
+<tp:spec
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+<tp:title>Miscellaneous extensions from the future</tp:title>
+
+<xi:include href="Call_Content_Codec_Offer.xml"/>
+<xi:include href="Call_Stream_Endpoint.xml"/>
+
+</tp:spec>
diff --git a/tools/c-constants-gen.py b/tools/c-constants-gen.py
new file mode 100644
index 0000000..09608c2
--- /dev/null
+++ b/tools/c-constants-gen.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+
+from sys import argv, stdout, stderr
+import xml.dom.minidom
+
+from libglibcodegen import NS_TP, get_docstring, \
+ get_descendant_text, get_by_path
+
+class Generator(object):
+ def __init__(self, prefix, dom, output_base):
+ self.prefix = prefix + '_'
+ self.spec = get_by_path(dom, "spec")[0]
+
+ self.__header = open(output_base + '.h', 'w')
+ self.__docs = open(output_base + '-gtk-doc.h', 'w')
+
+ def __call__(self):
+ self.do_header()
+ self.do_body()
+ self.do_footer()
+
+ def write(self, code):
+ self.__header.write(code.encode('utf-8'))
+
+ def d(self, code):
+ self.__docs.write(code.encode('utf-8'))
+
+ # Header
+ def do_header(self):
+ self.write('/* Generated from ')
+ self.write(get_descendant_text(get_by_path(self.spec, 'title')))
+ version = get_by_path(self.spec, "version")
+ if version:
+ self.write(', version ' + get_descendant_text(version))
+ self.write('\n\n')
+ for copyright in get_by_path(self.spec, 'copyright'):
+ self.write(get_descendant_text(copyright))
+ self.write('\n')
+ self.write(get_descendant_text(get_by_path(self.spec, 'license')))
+ self.write('\n')
+ self.write(get_descendant_text(get_by_path(self.spec, 'docstring')))
+ self.write("""
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+\n""")
+
+ # Body
+ def do_body(self):
+ for elem in self.spec.getElementsByTagNameNS(NS_TP, '*'):
+ if elem.localName == 'flags':
+ self.do_flags(elem)
+ elif elem.localName == 'enum':
+ self.do_enum(elem)
+
+ def do_flags(self, flags):
+ name = flags.getAttribute('plural') or flags.getAttribute('name')
+ value_prefix = flags.getAttribute('singular') or \
+ flags.getAttribute('value-prefix') or \
+ flags.getAttribute('name')
+ self.d("""\
+/**
+ *
+%s:
+""" % (self.prefix + name).replace('_', ''))
+ for flag in get_by_path(flags, 'flag'):
+ self.do_gtkdoc(flag, value_prefix)
+ self.d(' *\n')
+ docstrings = get_by_path(flags, 'docstring')
+ if docstrings:
+ self.d("""\
+ * <![CDATA[%s]]>
+ *
+""" % get_descendant_text(docstrings).replace('\n', ' '))
+ self.d("""\
+ * Bitfield/set of flags generated from the Telepathy specification.
+ */
+""")
+
+ self.write("typedef enum {\n")
+
+ for flag in get_by_path(flags, 'flag'):
+ self.do_val(flag, value_prefix)
+ self.write("""\
+} %s;
+
+""" % (self.prefix + name).replace('_', ''))
+
+ def do_enum(self, enum):
+ name = enum.getAttribute('singular') or enum.getAttribute('name')
+ value_prefix = enum.getAttribute('singular') or \
+ enum.getAttribute('value-prefix') or \
+ enum.getAttribute('name')
+ name_plural = enum.getAttribute('plural') or \
+ enum.getAttribute('name') + 's'
+ self.d("""\
+/**
+ *
+%s:
+""" % (self.prefix + name).replace('_', ''))
+ vals = get_by_path(enum, 'enumvalue')
+ for val in vals:
+ self.do_gtkdoc(val, value_prefix)
+ self.d(' *\n')
+ docstrings = get_by_path(enum, 'docstring')
+ if docstrings:
+ self.d("""\
+ * <![CDATA[%s]]>
+ *
+""" % get_descendant_text(docstrings).replace('\n', ' '))
+ self.d("""\
+ * Bitfield/set of flags generated from the Telepathy specification.
+ */
+""")
+
+ self.write("typedef enum {\n")
+
+ for val in vals:
+ self.do_val(val, value_prefix)
+ self.write("} %s;\n" % (self.prefix + name).replace('_', ''))
+
+ self.d("""\
+/**
+ * NUM_%(upper-plural)s:
+ *
+ * 1 higher than the highest valid value of #%(mixed-name)s.
+ */
+""" % {'mixed-name' : (self.prefix + name).replace('_', ''),
+ 'upper-plural' : (self.prefix + name_plural).upper(),
+ 'last-val' : vals[-1].getAttribute('value')})
+
+ self.write("""\
+#define NUM_%(upper-plural)s (%(last-val)s+1)
+
+""" % {'mixed-name' : (self.prefix + name).replace('_', ''),
+ 'upper-plural' : (self.prefix + name_plural).upper(),
+ 'last-val' : vals[-1].getAttribute('value')})
+
+ def do_val(self, val, value_prefix):
+ name = val.getAttribute('name')
+ suffix = val.getAttribute('suffix')
+ use_name = (self.prefix + value_prefix + '_' + \
+ (suffix or name)).upper()
+ assert not (name and suffix) or name == suffix, \
+ 'Flag/enumvalue name %s != suffix %s' % (name, suffix)
+ self.write(' %s = %s,\n' % (use_name, val.getAttribute('value')))
+
+ def do_gtkdoc(self, node, value_prefix):
+ self.d(' * @')
+ self.d((self.prefix + value_prefix + '_' +
+ node.getAttribute('suffix')).upper())
+ self.d(': <![CDATA[')
+ docstring = get_by_path(node, 'docstring')
+ self.d(get_descendant_text(docstring).replace('\n', ' '))
+ self.d(']]>\n')
+
+ # Footer
+ def do_footer(self):
+ self.write("""
+#ifdef __cplusplus
+}
+#endif
+""")
+
+if __name__ == '__main__':
+ argv = argv[1:]
+ Generator(argv[0], xml.dom.minidom.parse(argv[1]), argv[2])()
diff --git a/tools/glib-client-gen.py b/tools/glib-client-gen.py
new file mode 100644
index 0000000..6b0bdeb
--- /dev/null
+++ b/tools/glib-client-gen.py
@@ -0,0 +1,1224 @@
+#!/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. <http://www.collabora.co.uk/>
+#
+# 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, \
+ get_docstring, xml_escape, get_deprecated
+
+
+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.__docs = []
+
+ 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.tp_proxy_api = tuple(map(int,
+ opts.get('--tp-proxy-api', '0').split('.')))
+ 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 '
+ self.generate_reentrant = ('--generate-reentrant' in opts or
+ '--deprecate-reentrant' in opts)
+ self.deprecate_reentrant = opts.get('--deprecate-reentrant', None)
+ self.deprecation_attribute = opts.get('--deprecation-attribute',
+ 'G_GNUC_DEPRECATED')
+
+ def h(self, s):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ self.__header.append(s)
+
+ def b(self, s):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ self.__body.append(s)
+
+ def d(self, s):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ self.__docs.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 = signal.getAttribute('tp:name-for-bindings')
+ if member != member_lc.replace('_', ''):
+ raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
+ 'not match' % (member, member_lc))
+ member_lc = member_lc.lower()
+ 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.d('/**')
+ self.d(' * %s:' % callback_name)
+ self.d(' * @proxy: The proxy on which %s_%s_connect_to_%s ()'
+ % (self.prefix_lc, iface_lc, member_lc))
+ self.d(' * was called')
+
+ for arg in args:
+ name, info, tp_type, elt = arg
+ ctype, gtype, marshaller, pointer = info
+
+ self.d(' * @%s: %s' % (name,
+ xml_escape(get_docstring(elt) or '(Undocumented)')))
+
+ self.d(' * @user_data: User-supplied data')
+ self.d(' * @weak_object: User-supplied weakly referenced object')
+ self.d(' *')
+ self.d(' * Represents the signature of a callback for the signal %s.'
+ % member)
+ self.d(' */')
+ self.d('')
+
+ 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 G_GNUC_UNUSED,' % 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_uint64 (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 G_GNUC_UNUSED,')
+ 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.d('/**')
+ self.d(' * %s_%s_connect_to_%s:'
+ % (self.prefix_lc, iface_lc, member_lc))
+ self.d(' * @proxy: %s' % self.proxy_doc)
+ self.d(' * @callback: Callback to be called when the signal is')
+ self.d(' * received')
+ self.d(' * @user_data: User-supplied data for the callback')
+ self.d(' * @destroy: Destructor for the user-supplied data, which')
+ self.d(' * will be called when this signal is disconnected, or')
+ self.d(' * before this function returns %NULL')
+ self.d(' * @weak_object: A #GObject which will be weakly referenced; ')
+ self.d(' * if it is destroyed, this callback will automatically be')
+ self.d(' * disconnected')
+ self.d(' * @error: If not %NULL, used to raise an error if %NULL is')
+ self.d(' * returned')
+ self.d(' *')
+ self.d(' * Connect a handler to the signal %s.' % member)
+ self.d(' *')
+ self.d(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)'))
+ self.d(' *')
+ self.d(' * Returns: a #TpProxySignalConnection containing all of the')
+ self.d(' * above, which can be used to disconnect the signal; or')
+ self.d(' * %NULL if the proxy does not have the desired interface')
+ self.d(' * or has become invalid.')
+ self.d(' */')
+ self.d('')
+
+ 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.h('')
+
+ 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('')
+
+ def do_method(self, iface, method):
+ iface_lc = iface.lower()
+
+ member = method.getAttribute('name')
+ member_lc = method.getAttribute('tp:name-for-bindings')
+ if member != member_lc.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (member, member_lc))
+ member_lc = member_lc.lower()
+ 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.d('/**')
+ self.d(' * %s_%s_callback_for_%s:'
+ % (self.prefix_lc, iface_lc, member_lc))
+ self.d(' * @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.d(' * @%s: Used to return an \'out\' argument if @error is '
+ '%%NULL: %s'
+ % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
+
+ self.d(' * @error: %NULL on success, or an error on failure')
+ self.d(' * @user_data: user-supplied data')
+ self.d(' * @weak_object: user-supplied object')
+ self.d(' *')
+ self.d(' * Signature of the callback called when a %s method call'
+ % member)
+ self.d(' * succeeds or fails.')
+
+ deprecated = method.getElementsByTagName('tp:deprecated')
+ if deprecated:
+ d = deprecated[0]
+ self.d(' *')
+ self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d)))
+
+ self.d(' */')
+ self.d('')
+
+ 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.d('/**')
+ self.d(' * %s_%s_call_%s:'
+ % (self.prefix_lc, iface_lc, member_lc))
+ self.d(' * @proxy: the #TpProxy')
+ self.d(' * @timeout_ms: the timeout in milliseconds, or -1 to use the')
+ self.d(' * default')
+
+ for arg in in_args:
+ name, info, tp_type, elt = arg
+ ctype, gtype, marshaller, pointer = info
+
+ self.d(' * @%s: Used to pass an \'in\' argument: %s'
+ % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
+
+ self.d(' * @callback: called when the method call succeeds or fails;')
+ self.d(' * may be %NULL to make a "fire and forget" call with no ')
+ self.d(' * reply tracking')
+ self.d(' * @user_data: user-supplied data passed to the callback;')
+ self.d(' * must be %NULL if @callback is %NULL')
+ self.d(' * @destroy: called with the user_data as argument, after the')
+ self.d(' * call has succeeded, failed or been cancelled;')
+ self.d(' * must be %NULL if @callback is %NULL')
+ self.d(' * @weak_object: If not %NULL, a #GObject which will be ')
+ self.d(' * weakly referenced; if it is destroyed, this call ')
+ self.d(' * will automatically be cancelled. Must be %NULL if ')
+ self.d(' * @callback is %NULL')
+ self.d(' *')
+ self.d(' * Start a %s method call.' % member)
+ self.d(' *')
+ self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
+ self.d(' *')
+ self.d(' * Returns: a #TpProxyPendingCall representing the call in')
+ self.d(' * progress. It is borrowed from the object, and will become')
+ self.d(' * invalid when the callback is called, the call is')
+ self.d(' * cancelled or the #TpProxy becomes invalid.')
+
+ deprecated = method.getElementsByTagName('tp:deprecated')
+ if deprecated:
+ d = deprecated[0]
+ self.d(' *')
+ self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d)))
+
+ self.d(' */')
+ self.d('')
+
+ 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(' g_return_val_if_fail (callback != NULL || '
+ 'user_data == NULL, NULL);')
+ self.b(' g_return_val_if_fail (callback != NULL || '
+ 'destroy == NULL, NULL);')
+ self.b(' g_return_val_if_fail (callback != NULL || '
+ 'weak_object == NULL, NULL);')
+ 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('')
+ self.b(' if (destroy != NULL)')
+ self.b(' destroy (user_data);')
+ self.b('')
+ self.b(' g_error_free (error);')
+ 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('')
+
+ if self.generate_reentrant:
+ self.do_method_reentrant(method, iface_lc, member, member_lc,
+ in_args, out_args, collect_callback)
+
+ # leave a gap for the end of the method
+ self.d('')
+ self.b('')
+ self.h('')
+
+ def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args,
+ out_args, collect_callback):
+ # 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(' unsigned success:1;')
+ self.b(' unsigned 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 G_GNUC_UNUSED,' % reentrant_invoke)
+ self.b(' GError *error,')
+ self.b(' GValueArray *args,')
+ self.b(' GCallback unused G_GNUC_UNUSED,')
+ self.b(' gpointer user_data G_GNUC_UNUSED,')
+ self.b(' GObject *unused2 G_GNUC_UNUSED)')
+ 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('')
+
+ if self.deprecate_reentrant:
+ self.h('#ifndef %s' % self.deprecate_reentrant)
+
+ self.h('gboolean %s_%s_run_%s (%sproxy,'
+ % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg))
+ self.h(' gint timeout_ms,')
+
+ self.d('/**')
+ self.d(' * %s_%s_run_%s:' % (self.prefix_lc, iface_lc, member_lc))
+ self.d(' * @proxy: %s' % self.proxy_doc)
+ self.d(' * @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.d(' * @%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.d(' * @%s: Used to return an \'out\' argument if %%TRUE is '
+ 'returned: %s'
+ % (name, xml_escape(get_docstring(elt) or '(Undocumented)')))
+
+ self.d(' * @error: If not %NULL, used to return errors if %FALSE ')
+ self.d(' * is returned')
+ self.d(' * @loop: If not %NULL, set before re-entering ')
+ self.d(' * the main loop, to point to a #GMainLoop ')
+ self.d(' * which can be used to cancel this call with ')
+ self.d(' * g_main_loop_quit(), causing a return of ')
+ self.d(' * %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED')
+ self.d(' *')
+ self.d(' * Call the method %s and run the main loop' % member)
+ self.d(' * until it returns. Before calling this method, you must')
+ self.d(' * add a reference to any borrowed objects you need to keep,')
+ self.d(' * and generally ensure that everything is in a consistent')
+ self.d(' * state.')
+ self.d(' *')
+ self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)'))
+ self.d(' *')
+ self.d(' * Returns: TRUE on success, FALSE and sets @error on error')
+
+ deprecated = method.getElementsByTagName('tp:deprecated')
+ if deprecated:
+ d = deprecated[0]
+ self.d(' *')
+ self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d)))
+
+ self.d(' */')
+ self.d('')
+
+ 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,')
+
+ if self.deprecate_reentrant:
+ self.h(' GMainLoop **loop) %s;' % self.deprecation_attribute)
+ self.h('#endif /* not %s */' % self.deprecate_reentrant)
+ else:
+ 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('')
+
+ 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')
+
+ if signals:
+ self.b('static inline void')
+ self.b('%s_add_signals_for_%s (DBusGProxy *proxy)'
+ % (self.prefix_lc, name.lower()))
+ self.b('{')
+
+ if self.tp_proxy_api >= (0, 7, 6):
+ self.b(' if (!tp_proxy_dbus_g_proxy_claim_for_signal_adding '
+ '(proxy))')
+ self.b(' return;')
+
+ 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('/*<private_header>*/')
+ 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 G_GNUC_UNUSED,'
+ % (self.prefix_lc, self.group))
+ self.b(' guint quark,')
+ self.b(' DBusGProxy *proxy,')
+ self.b(' gpointer unused G_GNUC_UNUSED)')
+
+ self.b('{')
+
+ for node in nodes:
+ iface = node.getElementsByTagName('interface')[0]
+ self.iface_dbus = iface.getAttribute('name')
+ signals = node.getElementsByTagName('signal')
+ if not signals:
+ continue
+ 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))
+ open(self.basename + '-gtk-doc.h', 'w').write('\n'.join(self.__docs))
+
+
+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=', 'tp-proxy-api=',
+ 'generate-reentrant', 'deprecate-reentrant=',
+ 'deprecation-attribute='])
+
+ 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 0000000..5444725
--- /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-ginterface-gen.py b/tools/glib-ginterface-gen.py
new file mode 100644
index 0000000..04509fd
--- /dev/null
+++ b/tools/glib-ginterface-gen.py
@@ -0,0 +1,820 @@
+#!/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, \
+ 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 = []
+ self.__docs = []
+
+ 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.basename = basename
+ 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):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ self.__header.append(s)
+
+ def b(self, s):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ self.__body.append(s)
+
+ def d(self, s):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ self.__docs.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.d('/**')
+ self.d(' * %s%s:' % (self.Prefix, node_name_mixed))
+ self.d(' *')
+ self.d(' * Dummy typedef representing any implementation of this '
+ 'interface.')
+ self.d(' */')
+
+ self.h('typedef struct _%s%s %s%s;'
+ % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
+ self.h('')
+
+ self.d('/**')
+ self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed))
+ self.d(' *')
+ self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
+
+ if methods:
+ self.d(' *')
+ self.d(' * In a full implementation of this interface (i.e. all')
+ self.d(' * methods implemented), the interface initialization')
+ self.d(' * function used in G_IMPLEMENT_INTERFACE() would')
+ self.d(' * typically look like this:')
+ self.d(' *')
+ self.d(' * <programlisting>')
+ self.d(' * static void')
+ self.d(' * implement_%s (gpointer klass,' % self.node_name_lc)
+ self.d(' * gpointer unused G_GNUC_UNUSED)')
+ self.d(' * {')
+ self.d(' * #define IMPLEMENT(x) %s%s_implement_&num;&num;x (\\'
+ % (self.prefix_, self.node_name_lc))
+ self.d(' * klass, my_object_&num;&num;x)')
+
+ for method in methods:
+ class_member_name = method.getAttribute('tp:name-for-bindings')
+ class_member_name = class_member_name.lower()
+ self.d(' * IMPLEMENT (%s);' % class_member_name)
+
+ self.d(' * #undef IMPLEMENT')
+ self.d(' * }')
+ self.d(' * </programlisting>')
+ else:
+ self.d(' * This interface has no D-Bus methods, so an')
+ self.d(' * implementation can typically pass %NULL to')
+ self.d(' * G_IMPLEMENT_INTERFACE() as the interface')
+ self.d(' * initialization function.')
+
+ self.d(' */')
+ self.d('')
+
+ 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 G_GNUC_UNUSED)'
+ % (self.prefix_, node_name_lc))
+ self.b('{')
+
+ if properties:
+ 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(' 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('')
+
+ if properties:
+ 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('}')
+
+ 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)
+
+ if len(methods) == 0:
+ # empty arrays are a gcc extension, so put in a dummy member
+ self.b(" { NULL, NULL, 0 }")
+
+ 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 = method.getAttribute('tp:name-for-bindings')
+ if method.getAttribute('name') != lc_name.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (method.getAttribute('name'), lc_name))
+ lc_name = lc_name.lower()
+
+ 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 = method.getAttribute('tp:name-for-bindings')
+ if dbus_method_name != class_member_name.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (dbus_method_name, class_member_name))
+ class_member_name = class_member_name.lower()
+
+ stub_name = (self.prefix_ + self.node_name_lc + '_' +
+ class_member_name)
+ return (stub_name + '_impl', class_member_name + '_cb')
+
+ 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 = method.getAttribute('tp:name-for-bindings')
+ if dbus_method_name != class_member_name.replace('_', ''):
+ raise AssertionError('Method %s tp:name-for-bindings (%s) does '
+ 'not match' % (dbus_method_name, class_member_name))
+ class_member_name = class_member_name.lower()
+
+ # 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 separated)
+ self.d('/**')
+ self.d(' * %s:' % impl_name)
+ self.d(' * @self: The object implementing this interface')
+ for (ctype, name) in in_args:
+ self.d(' * @%s: %s (FIXME, generate documentation)'
+ % (name, ctype))
+ self.d(' * @context: Used to return values or throw an error')
+ self.d(' *')
+ self.d(' * The signature of an implementation of the D-Bus method')
+ self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
+ self.d(' */')
+
+ 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_cb);'
+ % (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.d('/**')
+ self.d(' * %s%s_implement_%s:'
+ % (self.prefix_, self.node_name_lc, class_member_name))
+ self.d(' * @klass: A class whose instances implement this interface')
+ self.d(' * @impl: A callback used to implement the %s D-Bus method'
+ % dbus_method_name)
+ self.d(' *')
+ self.d(' * Register an implementation for the %s method in the vtable'
+ % dbus_method_name)
+ self.d(' * of an implementation of this interface. To be called from')
+ self.d(' * the interface init function.')
+ self.d(' */')
+
+ 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_cb = impl;' % class_member_name)
+ self.b('}')
+ self.b('')
+
+ # Return convenience function (static inline, in header)
+ self.d('/**')
+ self.d(' * %s:' % ret_name)
+ self.d(' * @context: The D-Bus method invocation context')
+ for (ctype, name) in out_args:
+ self.d(' * @%s: %s (FIXME, generate documentation)'
+ % (name, ctype))
+ self.d(' *')
+ self.d(' * Return successfully by calling dbus_g_method_return().')
+ self.d(' * This inline function exists only to provide type-safety.')
+ self.d(' */')
+ self.d('')
+
+ 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')
+
+ ugly_name = signal.getAttribute('tp:name-for-bindings')
+ if dbus_name != ugly_name.replace('_', ''):
+ raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
+ 'not match' % (dbus_name, ugly_name))
+
+ stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
+ ugly_name.lower())
+
+ 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.d('/**')
+ self.d(' * %s:' % stub_name)
+ self.d(' * @instance: The object implementing this interface')
+ for (ctype, name, gtype) in args:
+ self.d(' * @%s: %s (FIXME, generate documentation)'
+ % (name, ctype))
+ self.d(' *')
+ self.d(' * Type-safe wrapper around g_signal_emit to emit the')
+ self.d(' * %s signal on interface %s.'
+ % (dbus_name, self.iface_name))
+ self.d(' */')
+
+ 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('')
+
+ signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
+ '-')
+
+ self.d('/**')
+ self.d(' * %s%s::%s:'
+ % (self.Prefix, self.node_name_mixed, signal_name))
+ for (ctype, name, gtype) in args:
+ self.d(' * @%s: %s (FIXME, generate documentation)'
+ % (name, ctype))
+ self.d(' *')
+ self.d(' * The %s D-Bus signal is emitted whenever '
+ 'this GObject signal is.' % dbus_name)
+ self.d(' */')
+ self.d('')
+
+ in_base_init.append(' %s_signals[%s] ='
+ % (self.node_name_lc, const_name))
+ in_base_init.append(' g_signal_new ("%s",' % signal_name)
+ 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 have_properties(self, nodes):
+ for node in nodes:
+ interface = node.getElementsByTagName('interface')[0]
+ if interface.getElementsByTagName('property'):
+ return True
+ return False
+
+ def __call__(self):
+ nodes = self.dom.getElementsByTagName('node')
+ nodes.sort(cmp_by_name)
+
+ self.h('#include <glib-object.h>')
+ self.h('#include <dbus/dbus-glib.h>')
+
+ if self.have_properties(nodes):
+ self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
+
+ self.h('')
+ self.h('G_BEGIN_DECLS')
+ self.h('')
+
+ self.b('#include "%s.h"' % self.basename)
+ self.b('')
+ for header in self.headers:
+ self.b('#include %s' % header)
+ self.b('')
+
+ 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(self.basename + '.h', 'w').write('\n'.join(self.__header))
+ open(self.basename + '.c', 'w').write('\n'.join(self.__body))
+ open(self.basename + '-gtk-doc.h', 'w').write('\n'.join(self.__docs))
+
+
+def cmdline_error():
+ print """\
+usage:
+ gen-ginterface [OPTIONS] xmlfile Prefix_
+options:
+ --include='<header.h>' (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 0000000..a49c36e
--- /dev/null
+++ b/tools/glib-gtypes-generator.py
@@ -0,0 +1,298 @@
+#!/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')
+ self.docs = open(output + '-gtk-doc.h', 'w')
+
+ for f in (self.header, self.body, self.docs):
+ 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')
+
+ # keys are e.g. 'sv', values are the key escaped
+ self.need_mappings = {}
+ # keys are the contents of the struct (e.g. 'sssu'), values are the
+ # key escaped
+ self.need_structs = {}
+ # keys are the contents of the struct (e.g. 'sssu'), values are the
+ # key escaped
+ self.need_struct_arrays = {}
+
+ # keys are the contents of the array (unlike need_struct_arrays!),
+ # values are the key escaped
+ self.need_other_arrays = {}
+
+ def h(self, code):
+ self.header.write(code.encode("utf-8"))
+
+ def c(self, code):
+ self.body.write(code.encode("utf-8"))
+
+ def d(self, code):
+ self.docs.write(code.encode('utf-8'))
+
+ 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.d('/**\n * %s:\n *\n' % name)
+ self.d(' * %s\n' % xml_escape(docstring))
+ self.d(' *\n')
+ self.d(' * This macro expands to a call to a function\n')
+ self.d(' * that returns the #GType of a #GHashTable\n')
+ self.d(' * appropriate for representing a D-Bus\n')
+ self.d(' * dictionary of signature\n')
+ self.d(' * <literal>a{%s}</literal>.\n' % impl_sig)
+ self.d(' *\n')
+
+ key, value = members
+
+ self.d(' * Keys (D-Bus type <literal>%s</literal>,\n'
+ % key.getAttribute('type'))
+ tp_type = key.getAttributeNS(NS_TP, 'type')
+ if tp_type:
+ self.d(' * type <literal>%s</literal>,\n' % tp_type)
+ self.d(' * named <literal>%s</literal>):\n'
+ % key.getAttribute('name'))
+ docstring = get_docstring(key) or '(Undocumented)'
+ self.d(' * %s\n' % xml_escape(docstring))
+ self.d(' *\n')
+
+ self.d(' * Values (D-Bus type <literal>%s</literal>,\n'
+ % value.getAttribute('type'))
+ tp_type = value.getAttributeNS(NS_TP, 'type')
+ if tp_type:
+ self.d(' * type <literal>%s</literal>,\n' % tp_type)
+ self.d(' * named <literal>%s</literal>):\n'
+ % value.getAttribute('name'))
+ docstring = get_docstring(value) or '(Undocumented)'
+ self.d(' * %s\n' % xml_escape(docstring))
+ self.d(' *\n')
+
+ self.d(' */\n')
+
+ self.h('#define %s (%s ())\n\n' % (name, impl))
+ self.need_mappings[impl_sig] = esc_impl_sig
+
+ array_name = mapping.getAttribute('array-name')
+ if array_name:
+ gtype_name = self.PREFIX_ + 'ARRAY_TYPE_' + array_name.upper()
+ contents_sig = 'a{' + impl_sig + '}'
+ esc_contents_sig = escape_as_identifier(contents_sig)
+ impl = self.prefix_ + 'type_dbus_array_of_' + esc_contents_sig
+ self.d('/**\n * %s:\n\n' % gtype_name)
+ self.d(' * Expands to a call to a function\n')
+ self.d(' * that returns the #GType of a #GPtrArray\n')
+ self.d(' * of #%s.\n' % name)
+ self.d(' */\n\n')
+
+ self.h('#define %s (%s ())\n\n' % (gtype_name, impl))
+ self.need_other_arrays[contents_sig] = esc_contents_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('<tp:docstring>'):
+ docstring = docstring[14:]
+ if docstring.endswith('</tp:docstring>\n'):
+ docstring = docstring[:-16]
+ if docstring.strip() in ('<tp:docstring/>', ''):
+ docstring = '(Undocumented)'
+ else:
+ docstring = '(Undocumented)'
+ self.d('/**\n * %s:\n\n' % name)
+ self.d(' * %s\n' % xml_escape(docstring))
+ self.d(' *\n')
+ self.d(' * This macro expands to a call to a function\n')
+ self.d(' * that returns the #GType of a #GValueArray\n')
+ self.d(' * appropriate for representing a D-Bus struct\n')
+ self.d(' * with signature <literal>(%s)</literal>.\n'
+ % impl_sig)
+ self.d(' *\n')
+
+ for i, member in enumerate(members):
+ self.d(' * Member %d (D-Bus type '
+ '<literal>%s</literal>,\n'
+ % (i, member.getAttribute('type')))
+ tp_type = member.getAttributeNS(NS_TP, 'type')
+ if tp_type:
+ self.d(' * type <literal>%s</literal>,\n' % tp_type)
+ self.d(' * named <literal>%s</literal>):\n'
+ % member.getAttribute('name'))
+ docstring = get_docstring(member) or '(Undocumented)'
+ self.d(' * %s\n' % xml_escape(docstring))
+ self.d(' *\n')
+
+ self.d(' */\n\n')
+
+ self.h('#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.d('/**\n * %s:\n\n' % array_name)
+ self.d(' * Expands to a call to a function\n')
+ self.d(' * that returns the #GType of a #GPtrArray\n')
+ self.d(' * of #%s.\n' % name)
+ self.d(' */\n\n')
+
+ self.h('#define %s (%s ())\n\n' % (array_name, impl))
+ self.need_struct_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.h('GType %stype_dbus_hash_%s (void);\n\n' %
+ (self.prefix_, self.need_mappings[sig]))
+ self.c('GType\n%stype_dbus_hash_%s (void)\n{\n' %
+ (self.prefix_, self.need_mappings[sig]))
+ self.c(' static GType t = 0;\n\n')
+ self.c(' if (G_UNLIKELY (t == 0))\n')
+ # FIXME: translate sig into two GTypes
+ items = tuple(Signature(sig))
+ gtypes = types_to_gtypes(items)
+ self.c(' t = dbus_g_type_get_map ("GHashTable", '
+ '%s, %s);\n' % (gtypes[0], gtypes[1]))
+ self.c(' return t;\n')
+ self.c('}\n\n')
+
+ for struct in structs:
+ self.do_struct_header(struct)
+
+ for sig in self.need_structs:
+ self.h('GType %stype_dbus_struct_%s (void);\n\n' %
+ (self.prefix_, self.need_structs[sig]))
+ self.c('GType\n%stype_dbus_struct_%s (void)\n{\n' %
+ (self.prefix_, self.need_structs[sig]))
+ self.c(' static GType t = 0;\n\n')
+ self.c(' if (G_UNLIKELY (t == 0))\n')
+ self.c(' t = dbus_g_type_get_struct ("GValueArray",\n')
+ items = tuple(Signature(sig))
+ gtypes = types_to_gtypes(items)
+ for gtype in gtypes:
+ self.c(' %s,\n' % gtype)
+ self.c(' G_TYPE_INVALID);\n')
+ self.c(' return t;\n')
+ self.c('}\n\n')
+
+ for sig in self.need_struct_arrays:
+ self.h('GType %stype_dbus_array_%s (void);\n\n' %
+ (self.prefix_, self.need_struct_arrays[sig]))
+ self.c('GType\n%stype_dbus_array_%s (void)\n{\n' %
+ (self.prefix_, self.need_struct_arrays[sig]))
+ self.c(' static GType t = 0;\n\n')
+ self.c(' if (G_UNLIKELY (t == 0))\n')
+ self.c(' t = dbus_g_type_get_collection ("GPtrArray", '
+ '%stype_dbus_struct_%s ());\n' %
+ (self.prefix_, self.need_struct_arrays[sig]))
+ self.c(' return t;\n')
+ self.c('}\n\n')
+
+ for sig in self.need_other_arrays:
+ self.h('GType %stype_dbus_array_of_%s (void);\n\n' %
+ (self.prefix_, self.need_other_arrays[sig]))
+ self.c('GType\n%stype_dbus_array_of_%s (void)\n{\n' %
+ (self.prefix_, self.need_other_arrays[sig]))
+ self.c(' static GType t = 0;\n\n')
+ self.c(' if (G_UNLIKELY (t == 0))\n')
+
+ if sig[:2] == 'a{' and sig[-1:] == '}':
+ # array of mappings
+ self.c(' t = dbus_g_type_get_collection ('
+ '"GPtrArray", '
+ '%stype_dbus_hash_%s ());\n' %
+ (self.prefix_, escape_as_identifier(sig[2:-1])))
+ elif sig[:2] == 'a(' and sig[-1:] == ')':
+ # array of arrays of struct
+ self.c(' t = dbus_g_type_get_collection ('
+ '"GPtrArray", '
+ '%stype_dbus_array_%s ());\n' %
+ (self.prefix_, escape_as_identifier(sig[2:-1])))
+ elif sig[:1] == 'a':
+ # array of arrays of non-struct
+ self.c(' t = dbus_g_type_get_collection ('
+ '"GPtrArray", '
+ '%stype_dbus_array_of_%s ());\n' %
+ (self.prefix_, escape_as_identifier(sig[1:])))
+ else:
+ raise AssertionError("array of '%s' not supported" % sig)
+
+ self.c(' return t;\n')
+ self.c('}\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-gen.py b/tools/glib-interfaces-gen.py
new file mode 100644
index 0000000..69c721b
--- /dev/null
+++ b/tools/glib-interfaces-gen.py
@@ -0,0 +1,197 @@
+#!/usr/bin/python
+
+from sys import argv, stdout, stderr
+import xml.dom.minidom
+
+from libglibcodegen import NS_TP, get_docstring, \
+ get_descendant_text, get_by_path
+
+class Generator(object):
+ def __init__(self, prefix, implfile, declfile, dom):
+ self.prefix = prefix + '_'
+
+ assert declfile.endswith('.h')
+ docfile = declfile[:-2] + '-gtk-doc.h'
+
+ self.impls = open(implfile, 'w')
+ self.decls = open(declfile, 'w')
+ self.docs = open(docfile, 'w')
+ self.spec = get_by_path(dom, "spec")[0]
+
+ def h(self, code):
+ self.decls.write(code.encode('utf-8'))
+
+ def c(self, code):
+ self.impls.write(code.encode('utf-8'))
+
+ def d(self, code):
+ self.docs.write(code.encode('utf-8'))
+
+ def __call__(self):
+ for f in self.h, self.c:
+ self.do_header(f)
+ self.do_body()
+
+ # Header
+ def do_header(self, f):
+ f('/* Generated from: ')
+ f(get_descendant_text(get_by_path(self.spec, 'title')))
+ version = get_by_path(self.spec, "version")
+ if version:
+ f(' version ' + get_descendant_text(version))
+ f('\n\n')
+ for copyright in get_by_path(self.spec, 'copyright'):
+ f(get_descendant_text(copyright))
+ f('\n')
+ f('\n')
+ f(get_descendant_text(get_by_path(self.spec, 'license')))
+ f(get_descendant_text(get_by_path(self.spec, 'docstring')))
+ f("""
+ */
+
+""")
+
+ # Body
+ def do_body(self):
+ for iface in self.spec.getElementsByTagName('interface'):
+ self.do_iface(iface)
+
+ def do_iface(self, iface):
+ parent_name = get_by_path(iface, '../@name')
+ self.d("""\
+/**
+ * %(IFACE_DEFINE)s:
+ *
+ * The interface name "%(name)s"
+ */
+""" % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \
+ parent_name).upper().replace('/', ''),
+ 'name' : iface.getAttribute('name')})
+
+ self.h("""
+#define %(IFACE_DEFINE)s \\
+"%(name)s"
+""" % {'IFACE_DEFINE' : (self.prefix + 'IFACE_' + \
+ parent_name).upper().replace('/', ''),
+ 'name' : iface.getAttribute('name')})
+
+ self.d("""
+/**
+ * %(IFACE_QUARK_DEFINE)s:
+ *
+ * Expands to a call to a function that returns a quark for the interface \
+name "%(name)s"
+ */
+""" % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \
+ parent_name).upper().replace('/', ''),
+ 'iface_quark_func' : (self.prefix + 'iface_quark_' + \
+ parent_name).lower().replace('/', ''),
+ 'name' : iface.getAttribute('name')})
+
+ self.h("""
+#define %(IFACE_QUARK_DEFINE)s \\
+ (%(iface_quark_func)s ())
+
+GQuark %(iface_quark_func)s (void);
+
+""" % {'IFACE_QUARK_DEFINE' : (self.prefix + 'IFACE_QUARK_' + \
+ parent_name).upper().replace('/', ''),
+ 'iface_quark_func' : (self.prefix + 'iface_quark_' + \
+ parent_name).lower().replace('/', ''),
+ 'name' : iface.getAttribute('name')})
+
+ self.c("""\
+GQuark
+%(iface_quark_func)s (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ {
+ quark = g_quark_from_static_string ("%(name)s");
+ }
+
+ return quark;
+}
+
+""" % {'iface_quark_func' : (self.prefix + 'iface_quark_' + \
+ parent_name).lower().replace('/', ''),
+ 'name' : iface.getAttribute('name')})
+
+ for prop in iface.getElementsByTagNameNS(None, 'property'):
+ self.d("""
+/**
+ * %(IFACE_PREFIX)s_%(PROP_UC)s:
+ *
+ * The fully-qualified property name "%(name)s.%(prop)s"
+ */
+""" % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \
+ parent_name).upper().replace('/', ''),
+ 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(),
+ 'name' : iface.getAttribute('name'),
+ 'prop' : prop.getAttribute('name'),
+ })
+
+ self.h("""
+#define %(IFACE_PREFIX)s_%(PROP_UC)s \\
+"%(name)s.%(prop)s"
+""" % {'IFACE_PREFIX' : (self.prefix + 'PROP_' + \
+ parent_name).upper().replace('/', ''),
+ 'PROP_UC': prop.getAttributeNS(NS_TP, "name-for-bindings").upper(),
+ 'name' : iface.getAttribute('name'),
+ 'prop' : prop.getAttribute('name'),
+ })
+
+
+ for prop in iface.getElementsByTagNameNS(NS_TP, 'contact-attribute'):
+ self.d("""
+/**
+ * %(TOKEN_PREFIX)s_%(TOKEN_UC)s:
+ *
+ * The fully-qualified contact attribute token name "%(name)s/%(prop)s"
+ */
+""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \
+ parent_name).upper().replace('/', ''),
+ 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"),
+ 'name' : iface.getAttribute('name'),
+ 'prop' : prop.getAttribute('name'),
+ })
+
+ self.h("""
+#define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\
+"%(name)s/%(prop)s"
+""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \
+ parent_name).upper().replace('/', ''),
+ 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"),
+ 'name' : iface.getAttribute('name'),
+ 'prop' : prop.getAttribute('name'),
+ })
+
+ for prop in iface.getElementsByTagNameNS(NS_TP, 'hct'):
+ if (prop.getAttribute('is-family') != "yes"):
+ self.d("""
+/**
+ * %(TOKEN_PREFIX)s_%(TOKEN_UC)s:
+ *
+ * The fully-qualified capability token name "%(name)s/%(prop)s"
+ */
+""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \
+ parent_name).upper().replace('/', ''),
+ 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"),
+ 'name' : iface.getAttribute('name'),
+ 'prop' : prop.getAttribute('name'),
+ })
+
+ self.h("""
+#define %(TOKEN_PREFIX)s_%(TOKEN_UC)s \\
+"%(name)s/%(prop)s"
+""" % {'TOKEN_PREFIX' : (self.prefix + 'TOKEN_' + \
+ parent_name).upper().replace('/', ''),
+ 'TOKEN_UC': prop.getAttributeNS(None, "name").upper().replace("-", "_").replace(".", "_"),
+ 'name' : iface.getAttribute('name'),
+ 'prop' : prop.getAttribute('name'),
+ })
+
+if __name__ == '__main__':
+ argv = argv[1:]
+ Generator(argv[0], argv[1], argv[2], xml.dom.minidom.parse(argv[3]))()
diff --git a/tools/glib-signals-marshal-gen.py b/tools/glib-signals-marshal-gen.py
new file mode 100644
index 0000000..0d02c13
--- /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/libglibcodegen.py b/tools/libglibcodegen.py
new file mode 100644
index 0000000..6a9d214
--- /dev/null
+++ b/tools/libglibcodegen.py
@@ -0,0 +1,172 @@
+"""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-2008 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 libtpcodegen import NS_TP, \
+ Signature, \
+ cmp_by_name, \
+ escape_as_identifier, \
+ get_by_path, \
+ get_descendant_text, \
+ get_docstring, \
+ xml_escape, \
+ get_deprecated
+
+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 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
+
+
+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 ("GPtrArray *",
+ 'dbus_g_type_get_collection ("GPtrArray",'
+ ' DBUS_TYPE_G_OBJECT_PATH)',
+ "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
diff --git a/tools/libtpcodegen.py b/tools/libtpcodegen.py
new file mode 100644
index 0000000..837ff2f
--- /dev/null
+++ b/tools/libtpcodegen.py
@@ -0,0 +1,215 @@
+"""Library code for language-independent 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-2008 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 cmp_by_name(node1, node2):
+ return cmp(node1.getAttributeNode("name").nodeValue,
+ node2.getAttributeNode("name").nodeValue)
+
+
+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_by_path(element, path):
+ branches = path.split('/')
+ branch = branches[0]
+
+ # Is the current branch an attribute, if so, return the attribute value
+ if branch[0] == '@':
+ return element.getAttribute(branch[1:])
+
+ # Find matching children for the branch
+ children = []
+ if branch == '..':
+ children.append(element.parentNode)
+ else:
+ for x in element.childNodes:
+ if x.localName == branch:
+ children.append(x)
+
+ ret = []
+ # If this is not the last path element, recursively gather results from
+ # children
+ if len(branches) > 1:
+ for x in children:
+ add = get_by_path(x, '/'.join(branches[1:]))
+ if isinstance(add, list):
+ ret += add
+ else:
+ return add
+ else:
+ ret = children
+
+ return 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('<tp:docstring>'):
+ docstring = docstring[14:].lstrip()
+ if docstring.endswith('</tp:docstring>'):
+ docstring = docstring[:-15].rstrip()
+ if docstring in ('<tp:docstring/>', ''):
+ docstring = ''
+ return docstring
+
+def get_deprecated(element):
+ text = []
+ for x in element.childNodes:
+ if hasattr(x, 'data'):
+ text.append(x.data.replace('\n', ' ').strip())
+ else:
+ # This caters for tp:dbus-ref elements, but little else.
+ if x.childNodes and hasattr(x.childNodes[0], 'data'):
+ text.append(x.childNodes[0].data.replace('\n', ' ').strip())
+ return ' '.join(text)
+
+def get_descendant_text(element_or_elements):
+ if not element_or_elements:
+ return ''
+ if isinstance(element_or_elements, list):
+ return ''.join(map(get_descendant_text, element_or_elements))
+ parts = []
+ for x in element_or_elements.childNodes:
+ if x.nodeType == x.TEXT_NODE:
+ parts.append(x.nodeValue)
+ elif x.nodeType == x.ELEMENT_NODE:
+ parts.append(get_descendant_text(x))
+ else:
+ pass
+ return ''.join(parts)
+
+
+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 xml_escape(s):
+ s = s.replace('&', '&amp;').replace("'", '&apos;').replace('"', '&quot;')
+ return s.replace('<', '&lt;').replace('>', '&gt;')
diff --git a/tools/xincludator.py b/tools/xincludator.py
new file mode 100644
index 0000000..63e106a
--- /dev/null
+++ b/tools/xincludator.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+from sys import argv, stdout, stderr
+import codecs, locale
+import os
+import xml.dom.minidom
+
+stdout = codecs.getwriter('utf-8')(stdout)
+
+NS_XI = 'http://www.w3.org/2001/XInclude'
+
+def xincludate(dom, base, dropns = []):
+ remove_attrs = []
+ for i in xrange(dom.documentElement.attributes.length):
+ attr = dom.documentElement.attributes.item(i)
+ if attr.prefix == 'xmlns':
+ if attr.localName in dropns:
+ remove_attrs.append(attr)
+ else:
+ dropns.append(attr.localName)
+ for attr in remove_attrs:
+ dom.documentElement.removeAttributeNode(attr)
+ for include in dom.getElementsByTagNameNS(NS_XI, 'include'):
+ href = include.getAttribute('href')
+ # FIXME: assumes Unixy paths
+ filename = os.path.join(os.path.dirname(base), href)
+ subdom = xml.dom.minidom.parse(filename)
+ xincludate(subdom, filename, dropns)
+ if './' in href:
+ subdom.documentElement.setAttribute('xml:base', href)
+ include.parentNode.replaceChild(subdom.documentElement, include)
+
+if __name__ == '__main__':
+ argv = argv[1:]
+ dom = xml.dom.minidom.parse(argv[0])
+ xincludate(dom, argv[0])
+ xml = dom.toxml()
+ stdout.write(xml)
+ stdout.write('\n')