From 0a57d9bd5e8b6c0ec7fb29e60062aae1f9e6ebf6 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Fri, 7 Dec 2012 16:42:54 +0100
Subject: [PATCH] Add idle-inhibit D-Bus service spec

---
 idle-inhibit/.gitignore                      |    3 +
 idle-inhibit/Makefile                        |   17 +
 idle-inhibit/README                          |   20 +
 idle-inhibit/docbook-params.xsl              |   39 +
 idle-inhibit/html/style.css                  |  127 +++
 idle-inhibit/org.freedesktop.ScreenSaver.xml |   38 +
 idle-inhibit/specification.xml               |  114 +++
 idle-inhibit/tools/resolve-type.xsl          |  122 +++
 idle-inhibit/tools/spec-to-docbook.xsl       | 1242 ++++++++++++++++++++++++++
 idle-inhibit/tools/spec-to-introspect.xsl    |  144 +++
 10 files changed, 1866 insertions(+)
 create mode 100644 idle-inhibit/.gitignore
 create mode 100644 idle-inhibit/Makefile
 create mode 100644 idle-inhibit/README
 create mode 100644 idle-inhibit/docbook-params.xsl
 create mode 100644 idle-inhibit/html/style.css
 create mode 100644 idle-inhibit/org.freedesktop.ScreenSaver.xml
 create mode 100644 idle-inhibit/specification.xml
 create mode 100644 idle-inhibit/tools/resolve-type.xsl
 create mode 100644 idle-inhibit/tools/spec-to-docbook.xsl
 create mode 100644 idle-inhibit/tools/spec-to-introspect.xsl

diff --git a/idle-inhibit/.gitignore b/idle-inhibit/.gitignore
new file mode 100644
index 0000000..551e415
--- /dev/null
+++ b/idle-inhibit/.gitignore
@@ -0,0 +1,3 @@
+
+/html/*.html
+reference.xml
diff --git a/idle-inhibit/Makefile b/idle-inhibit/Makefile
new file mode 100644
index 0000000..8c651ba
--- /dev/null
+++ b/idle-inhibit/Makefile
@@ -0,0 +1,17 @@
+
+SPEC = org.freedesktop.ScreenSaver.xml
+
+all: html/index.html
+
+html/index.html: reference.xml docbook-params.xsl specification.xml
+	xmlto --skip-validation -o html/ -x docbook-params.xsl xhtml specification.xml
+
+reference.xml: tools/spec-to-docbook.xsl $(SPEC)
+	xsltproc tools/spec-to-docbook.xsl $(SPEC) > $@
+
+clean:
+	rm -f reference.xml
+	rm -f html/*.html
+
+upload: all
+	rsync -Hvax html/./ specs.freedesktop.org:/srv/specifications.freedesktop.org/www/idle-inhibition-service/./
diff --git a/idle-inhibit/README b/idle-inhibit/README
new file mode 100644
index 0000000..72a9997
--- /dev/null
+++ b/idle-inhibit/README
@@ -0,0 +1,20 @@
+Generating the specification consists of separate steps:
+
+1. Use an xslt processor to create a DocBook XML document from the Telepathy
+   Introspection document using the provided stylesheet:
+
+$ xsltproc tools/spec-to-docbook.xsl \
+    org.freedesktop.Secrets.xml >reference.xml
+
+2. Convert specification.xml and reference.xml to a suitable output format
+   (specification.xml includes reference.xml), eg html:
+
+$ xmlto --skip-validation -o html -p params-html.xsl xhtml specification.xml
+
+If you choose a different directory for html generation, be sure to copy
+html/style.css to that location.
+
+Then open the resulting html/index.html in your favourite browser.
+
+
+Michael Leupold <lemma@confuego.org>
diff --git a/idle-inhibit/docbook-params.xsl b/idle-inhibit/docbook-params.xsl
new file mode 100644
index 0000000..5d8591a
--- /dev/null
+++ b/idle-inhibit/docbook-params.xsl
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!--
+    Parameters for DocBook transformation.
+
+    Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+    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
+-->
+
+    <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl"/>
+
+    <xsl:param name="toc.max.depth">3</xsl:param>
+    <xsl:param name="generate.section.toc.level">0</xsl:param>
+    <xsl:param name="generate.toc">
+        book     toc
+        part     nop
+        chapter  toc
+    </xsl:param>
+    <xsl:param name="html.stylesheet">style.css</xsl:param>
+    <xsl:param name="funcsynopsis.style">ansi</xsl:param>
+    <xsl:param name="funcsynopsis.decoration">1</xsl:param>
+    <xsl:param name="refentry.generate.name">0</xsl:param>
+    <xsl:param name="refentry.generate.title">1</xsl:param>
+
+</xsl:stylesheet>
diff --git a/idle-inhibit/html/style.css b/idle-inhibit/html/style.css
new file mode 100644
index 0000000..f8a03b0
--- /dev/null
+++ b/idle-inhibit/html/style.css
@@ -0,0 +1,127 @@
+/* reference.css, a stylesheet for reference documentation
+ * generated by the DocBook XSL Stylesheets */
+/* $Id: reference.css 8234 2009-02-09 12:10:48Z xmldoc $ */
+
+div.legalnotice {
+  font-size: 80%;
+}
+
+div.note, div.tip, div.warning {
+  margin-left: 5%;
+  margin-right: 10%;
+  padding: 5px;
+}
+
+div.note, div.tip {
+  border-left: solid #d5dee3 20px;
+  border-right: solid #d5dee3 20px;
+}
+
+div.note, div.tip {
+  border-left: solid palegreen 20px;
+  border-right: solid palegreen 20px;
+}
+
+div.warning {
+  border-left: solid yellow 20px;
+  border-right: solid yellow 20px;
+}
+
+div.note p, div.tip p, div.warning p {
+  margin-top: 0px;
+  margin-bottom: 4px;
+}
+
+div.note h3, div.tip h3, div.warning h3 {
+  margin-top: 0;
+}
+
+div.informalexample {
+  background-color: #d5dee3;
+  border-top-width: 2px;
+  border-top-style: double;
+  border-top-color: #d3d3d3;
+  border-bottom-width: 2px;
+  border-bottom-style: double;
+  border-bottom-color: #d3d3d3;
+  padding: 4px;
+  margin: 0em;
+  margin-left: 2em;
+}
+
+pre.programlisting, pre.synopsis {
+  whitespace: pre;
+  font-family: monospace;
+  background-color: #d5dee3;
+  border-top-width: 1px;
+  border-top-style: single;
+  border-top-color: #d3d3d3;
+  border-bottom-width: 1px;
+  border-bottom-style: single;
+  border-bottom-color: #d3d3d3;
+  padding: 4px;
+  margin: 0em;
+  margin-top: 6px;
+  margin-bottom: 6px;
+}
+
+div.informalexample pre {
+  whitespace: pre;
+  font-family: monospace;
+  border-top-width: 0px;
+  border-bottom-width: 0px;
+  padding: 0px;
+}
+
+/* Parameter and PI titles */
+  div.refnamediv h2 {
+  font-size: 2em;
+}
+
+div.funcsynopsis, code.fieldsynopsis,
+div.refsect2 div.refsynopsisdiv {
+  padding: 0.5em;
+  background-color: #F4F4F4;
+  border: 1px solid gray;
+  display: block;
+  width: 80%;
+}
+
+div.refsynopsisdiv code.fieldsynopsis {
+  padding: 0;
+  border: 0;
+}
+
+code.fieldsynopsis span.modifier {
+  min-width: 8em;
+  display: inline-block;
+}
+
+code.fieldsynopsis span.type {
+  min-width: 11em;
+  display: inline-block;
+}
+
+code.fieldsynopsis span.varname {
+  min-width: 11em;
+  display: inline-block;
+  font-size: 1.1em;
+  font-weight: bold;
+}
+
+div.funcprototype-spacer {
+  max-height: 0.5em;
+}
+
+table.funcprototype-table * td {
+  font-size: 0.9em;
+}
+
+table.funcprototype-table * td code.funcdef {
+  font-size: 1.1em;
+}
+
+b.fsfunc {
+  display: inline-block;
+  min-width: 11em;
+}
\ No newline at end of file
diff --git a/idle-inhibit/org.freedesktop.ScreenSaver.xml b/idle-inhibit/org.freedesktop.ScreenSaver.xml
new file mode 100644
index 0000000..23278ad
--- /dev/null
+++ b/idle-inhibit/org.freedesktop.ScreenSaver.xml
@@ -0,0 +1,38 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<tp:spec xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+    <tp:title>Idle Inhibition Service API Specification</tp:title>
+    <tp:version>0.1</tp:version>
+    <tp:copyright>
+      Copyright (C) 2012 Bastien Nocera &lt;hadess@hadess.net&gt;
+    </tp:copyright>
+<node name="/org/freedesktop/ScreenSaver">
+    <interface name="org.freedesktop.ScreenSaver">
+    <tp:docstring>
+        The Idle Inhibition Service manages the inhibition requests.
+    </tp:docstring>
+
+    <method name="Inhibit">
+      <tp:docstring>Inhibit idleness for the caller application.</tp:docstring>
+
+      <arg name="application_name" type="s" direction="in">
+         <tp:docstring>A unique identifier for the application, usually a reverse domain (such as 'org.freedesktop.example').</tp:docstring>
+      </arg>
+
+      <arg name="reason_for_inhibit" type="s" direction="in">
+        <tp:docstring>A human-readable and possibly translated string explaining the reason why idleness is inhibited (such as 'Playing a movie').</tp:docstring>
+      </arg>
+
+      <arg name="cookie" type="u" direction="out">
+        <tp:docstring>A cookie uniquely representing the inhibition request, to be passed to UnInhibit when done.</tp:docstring>
+      </arg>
+    </method>
+    <method name="UnInhibit">
+      <tp:docstring>Disable inhibit idleness for the caller application.</tp:docstring>
+
+      <arg name="cookie" type="u" direction="in">
+        <tp:docstring>A cookie representing the inhibition request, as returned by the 'Inhibit' function.</tp:docstring>
+      </arg>
+    </method>
+  </interface>
+</node>
+</tp:spec>
diff --git a/idle-inhibit/specification.xml b/idle-inhibit/specification.xml
new file mode 100644
index 0000000..48bff67
--- /dev/null
+++ b/idle-inhibit/specification.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0"?>
+<book xml:id="index" xmlns="http://docbook.org/ns/docbook" version="5.0">
+	<bookinfo>
+		<title>Idle Inhibition Service Draft</title>
+		<releaseinfo>
+			Idle Inhibition Service 0.1 DRAFT
+		</releaseinfo>
+
+		<authorgroup>
+		<author>
+			<firstname>Bastien</firstname>
+			<surname>Nocera</surname>
+			<affiliation>
+				<jobtitle>GNOME core Developer</jobtitle>
+				<address>
+					<email>hadess@hadess.net</email>
+				</address>
+			</affiliation>
+		</author>
+		</authorgroup>
+
+		<copyright>
+			<year>2012</year>
+			<holder>The Idle Inhibition Service API Authors</holder>
+		</copyright>
+
+	</bookinfo>
+
+	<part xml:id="description">
+		<title>API Documentation</title>
+		<chapter xml:id="introduction">
+			<title>Introduction</title>
+
+			<para>The Idle Inhibition Service API allows client applications to inhibit a
+				user's login session from becoming idle.</para>
+
+			<para>The service is usually implemented by the session manager, or anothe daemon
+			running inside the user's session, and communicating with the platform software
+			to avoid the idle, and the consequences of becoming idle.</para>
+
+			<para>This API was designed by GNOME and KDE developers with the goal of having
+			a common way to inhibit idleness.</para>
+		</chapter>
+
+		<chapter xml:id="consequences">
+			<title>Consequences of idleness</title>
+
+			<para>Depending on the user session software running, idleness can have multiple
+			consequences, including, but not limited to:
+			<segmentedlist>
+				<?dbhtml list-presentation="list"?>
+				<seglistitem>Screen backlight dimming</seglistitem>
+				<seglistitem>Screensaver animation replacing the desktop content</seglistitem>
+				<seglistitem>Instant messenging client going to "auto-away"</seglistitem>
+				<seglistitem>Computer suspending or hibernating</seglistitem>
+			</segmentedlist>
+			</para>
+
+			<para>Inhibiting idleness is supposed to stop all those actions from taking place, specifically
+			in response to idleness. A user action asking for any of those states would obviously
+			be acted upon witout delay.</para>.
+
+		</chapter>
+
+		<chapter xml:id='api'>
+			<title>API documentation</title>
+
+			<para>Idle inhibition is achieved by the application calling an
+			<link linked="org.freedesktop.ScreenSaver.Inhibit"><function>Inhibit</function></link>
+			function on a well-known D-Bus name.</para>
+
+			<para>Inhibition will stop when the <link linked="org.freedesktop.ScreenSaver.UnInhibit"><function>
+			UnInhibit</function></link> function is called, or the application disconnects from the D-Bus
+			session bus (which usually happens upon exit).</para>
+		</chapter>
+
+		<chapter>
+			<title>API notes</title>
+
+			<para>The D-Bus service name is intentionally, as KDE implements the idle inhibition
+			API in a screensaver sub-module. It was necessary for the specificed Idle Inhibition
+			Service API to be compatible with this existing software.</para>
+
+			<para>For the same reasons, inhibition of suspend, hibernation, or user-switching (amongst
+			others) is not supported in the API.</para>
+		</chapter>
+
+	</part>
+
+	<part xml:id="ref-dbus-api">
+		<title>D-Bus API Reference</title>
+
+		<xi:include href="reference.xml" xpointer="interfaces" xmlns:xi="http://www.w3.org/2001/XInclude">
+			<xi:fallback/>
+		</xi:include>
+
+	</part>
+
+	<bibliography>
+		<title>References</title>
+
+		<bibliomixed>
+			<abbrev>kde-api</abbrev>
+			<ulink url="https://projects.kde.org/projects/kde/kde-workspace/repository/revisions/master/entry/ksmserver/screenlocker/dbus/org.freedesktop.ScreenSaver.xml">KDE Screenlocker D-Bus API</ulink>
+		</bibliomixed>
+
+		<bibliomixed>
+			<abbrev>gnome-impl</abbrev>
+			<ulink url="http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/screensaver-proxy">GNOME org.freedesktop.ScreenSaver proxy implementation</ulink>
+		</bibliomixed>
+
+	</bibliography>
+
+</book>
diff --git a/idle-inhibit/tools/resolve-type.xsl b/idle-inhibit/tools/resolve-type.xsl
new file mode 100644
index 0000000..a37f2af
--- /dev/null
+++ b/idle-inhibit/tools/resolve-type.xsl
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  exclude-result-prefixes="tp html">
+
+<!--
+    Helper templates for Telepathy D-Bus Introspection conversion.
+
+    Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+    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
+-->
+
+    <!-- Resolve the type a node has. This will first look at tp:type and
+        - if not found - use the type attribute -->
+    <xsl:template name="ResolveType">
+        <xsl:param name="node"/>
+        <xsl:variable name="unstripped">
+            <xsl:choose>
+                <xsl:when test="$node//@tp:type">
+                    <xsl:call-template name="TpType">
+                        <xsl:with-param name="type" select="$node//@tp:type"/>
+                    </xsl:call-template>
+                </xsl:when>
+                <xsl:when test="$node//@type">
+                    <xsl:call-template name="DBusType">
+                        <xsl:with-param name="type" select="$node//@type"/>
+                    </xsl:call-template>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:message terminate="yes">
+                        Node doesn't contain a type.
+                    </xsl:message>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:variable>
+        <xsl:value-of select="translate(translate($unstripped, ' ', ''), '&#xa;', '')"/>
+    </xsl:template>
+
+    <!-- Map a D-Bus type to its EggDBus counterpart -->
+    <xsl:template name="DBusType">
+        <xsl:param name="type"/>
+        <xsl:choose>
+            <xsl:when test="$type='o'">ObjectPath</xsl:when>
+            <xsl:when test="$type='s'">String</xsl:when>
+            <xsl:when test="$type='y'">Byte</xsl:when>
+            <xsl:when test="$type='b'">Boolean</xsl:when>
+            <xsl:when test="$type='n'">Int16</xsl:when>
+            <xsl:when test="$type='q'">UInt16</xsl:when>
+            <xsl:when test="$type='i'">Int32</xsl:when>
+            <xsl:when test="$type='u'">UInt32</xsl:when>
+            <xsl:when test="$type='x'">Int64</xsl:when>
+            <xsl:when test="$type='t'">UInt64</xsl:when>
+            <xsl:when test="$type='d'">Double</xsl:when>
+            <xsl:when test="$type='g'">Signature</xsl:when>
+            <xsl:when test="$type='v'">Variant</xsl:when>
+            <xsl:when test="starts-with($type, 'a{')">
+                Dict&lt;
+                <xsl:call-template name="DBusType">
+                    <xsl:with-param name="type" select="substring($type, 3, 1)"/>
+                </xsl:call-template>
+                ,
+                <xsl:call-template name="DBusType">
+                    <xsl:with-param name="type" select="substring($type, 4, 1)"/>
+                </xsl:call-template>
+                &gt;
+            </xsl:when>
+            <xsl:when test="starts-with($type, 'a')">
+                Array&lt;
+                <xsl:call-template name="DBusType">
+                    <xsl:with-param name="type" select="substring($type, 2)"/>
+                </xsl:call-template>
+                &gt;
+            </xsl:when>
+            <!-- TODO: doesn't implement dict-entries and structs -->
+            <xsl:otherwise>
+                <xsl:message terminate="yes">
+                    Unknown DBus Type <xsl:value-of select="$type"/>
+                </xsl:message>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <!-- Resolve tp:type attributes by searching for matching tp:struct
+            and tp:mapping elements -->
+    <xsl:template name="TpType">
+        <xsl:param name="type"/>
+        <xsl:choose>
+            <xsl:when test="/tp:spec/tp:struct[@name=$type]">
+                <xsl:value-of select="$type"/>
+            </xsl:when>
+            <xsl:when test="/tp:spec/tp:mapping[@name=$type]">
+                Dict&lt;
+                <xsl:call-template name="ResolveType">
+                    <xsl:with-param name="node" select="/tp:spec/tp:mapping[@name=$type]/tp:member[@name='Key']"/>
+                </xsl:call-template>,
+                <xsl:call-template name="ResolveType">
+                    <xsl:with-param name="node" select="/tp:spec/tp:mapping[@name=$type]/tp:member[@name='Value']"/>
+                </xsl:call-template>
+                &gt;
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:message terminate="yes">
+                    Unspecified type <xsl:value-of select="$type"/>.
+                </xsl:message>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+</xsl:stylesheet>
diff --git a/idle-inhibit/tools/spec-to-docbook.xsl b/idle-inhibit/tools/spec-to-docbook.xsl
new file mode 100644
index 0000000..a6b18a3
--- /dev/null
+++ b/idle-inhibit/tools/spec-to-docbook.xsl
@@ -0,0 +1,1242 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+  xmlns:docbook="http://docbook.org/ns/docbook"
+  exclude-result-prefixes="tp html">
+
+<!--
+    Telepathy D-Bus Introspection to Docbook XML translator.
+    Based on Telepathy's doc-generator.xsl.
+
+    Copyright (C) 2006-2008 Collabora Limited
+    Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+    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
+-->
+
+  <xsl:output method="xml" indent="yes" encoding="ascii"
+    omit-xml-declaration="no"/>
+
+  <xsl:include href="resolve-type.xsl"/>
+
+  <xsl:param name="allow-undefined-interfaces" select="false()"/>
+
+  <xsl:template match="docbook:* | html:* | @*">
+    <xsl:copy>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template name="direction">
+    <xsl:param name="indirection"/>
+    <xsl:choose>
+      <xsl:when test="$indirection = 'in'">IN</xsl:when>
+      <xsl:otherwise>OUT</xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="tp:type">
+    <xsl:call-template name="tp-type">
+      <xsl:with-param name="tp-type" select="string(.)"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <!-- tp:dbus-ref: reference a D-Bus interface, signal, method or property -->
+  <xsl:template match="tp:dbus-ref">
+    <xsl:variable name="name">
+      <xsl:choose>
+        <xsl:when test="@namespace">
+          <xsl:value-of select="@namespace"/>
+          <xsl:text>.</xsl:text>
+        </xsl:when>
+      </xsl:choose>
+      <xsl:value-of select="string(.)"/>
+    </xsl:variable>
+
+    <xsl:choose>
+      <xsl:when test="//interface[@name=$name]
+        or //interface/method[concat(../@name, '.', @name)=$name]
+        or //interface/signal[concat(../@name, '.', @name)=$name]
+        or //interface/property[concat(../@name, '.', @name)=$name]
+        or //interface[@name=concat($name, '.DRAFT')]
+        or //interface/method[
+          concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+        or //interface/signal[
+          concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+        or //interface/property[
+          concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+        ">
+        <link linkend="{$name}">
+          <literal><xsl:value-of select="$name"/></literal>
+        </link>
+      </xsl:when>
+
+      <xsl:when test="$allow-undefined-interfaces">
+        <!-- TODO: Convert to docbook -->
+        <span xmlns="http://www.w3.org/1999/xhtml" title="defined elsewhere">
+          <xsl:value-of select="string(.)"/>
+        </span>
+      </xsl:when>
+
+      <xsl:otherwise>
+        <xsl:message terminate="yes">
+          <xsl:text>ERR: cannot find D-Bus interface, method, </xsl:text>
+          <xsl:text>signal or property called '</xsl:text>
+          <xsl:value-of select="$name"/>
+          <xsl:text>'&#10;</xsl:text>
+        </xsl:message>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- tp:member-ref: reference a property of the current interface -->
+  <xsl:template match="tp:member-ref">
+    <xsl:variable name="prefix" select="concat(ancestor::interface/@name,
+      '.')"/>
+    <xsl:variable name="name" select="string(.)"/>
+
+    <xsl:if test="not(ancestor::interface)">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: Cannot use tp:member-ref when not in an</xsl:text>
+        <xsl:text> &lt;interface&gt;&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:choose>
+      <xsl:when test="ancestor::interface/signal[@name=$name]"/>
+      <xsl:when test="ancestor::interface/method[@name=$name]"/>
+      <xsl:when test="ancestor::interface/property[@name=$name]"/>
+      <xsl:otherwise>
+        <xsl:message terminate="yes">
+          <xsl:text>ERR: interface </xsl:text>
+          <xsl:value-of select="ancestor::interface/@name"/>
+          <xsl:text> has no signal/method/property called </xsl:text>
+          <xsl:value-of select="$name"/>
+          <xsl:text>&#10;</xsl:text>
+        </xsl:message>
+      </xsl:otherwise>
+    </xsl:choose>
+
+    <link linkend="{$prefix}{$name}">
+      <literal><xsl:value-of select="concat($prefix, $name)"/></literal>
+    </link>
+  </xsl:template>
+
+  <xsl:template match="*" mode="identity">
+    <xsl:copy>
+      <xsl:apply-templates mode="identity"/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="tp:docstring">
+    <para>
+      <xsl:copy-of select="child::node()"/>
+    </para>
+  </xsl:template>
+
+  <xsl:template match="tp:docstring" mode="nopara">
+    <xsl:copy-of select="child::node()"/>
+  </xsl:template>
+
+  <xsl:template match="tp:added">
+    <para>
+      Added in version <xsl:value-of select="@version"/>.
+      <xsl:apply-templates select="node()"/>
+    </para>
+  </xsl:template>
+
+  <xsl:template match="tp:changed">
+    <xsl:choose>
+      <xsl:when test="node()">
+        <para>
+          Changed in version <xsl:value-of select="@version"/>:
+          <xsl:apply-templates select="node()"/></para>
+      </xsl:when>
+      <xsl:otherwise>
+        <para>Changed in version
+          <xsl:value-of select="@version"/></para>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="tp:deprecated">
+    <para>
+      Deprecated since version <xsl:value-of select="@version"/>.
+      <xsl:apply-templates select="node()"/>
+    </para>
+  </xsl:template>
+
+  <xsl:template match="tp:rationale">
+    <!-- TODO: special? -->
+    <para>
+      <xsl:apply-templates select="node()"/>
+    </para>
+  </xsl:template>
+
+  <xsl:template match="tp:errors">
+    <title>Errors</title>
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template name="generic-types">
+    <chapter>
+      <xsl:attribute name="xml:id">types</xsl:attribute>
+        <title>Types</title>
+        <xsl:call-template name="do-types"/>
+    </chapter>
+  </xsl:template>
+
+  <xsl:template name="do-types">
+    <xsl:if test="tp:simple-type">
+      <section>
+        <title>Simple types</title>
+        <xsl:apply-templates select="tp:simple-type"/>
+      </section>
+    </xsl:if>
+
+    <xsl:if test="tp:enum">
+      <section>
+        <title>Enumerated types</title>
+        <xsl:apply-templates select="tp:enum"/>
+      </section>
+    </xsl:if>
+
+    <xsl:if test="tp:flags">
+      <section>
+        <title>Sets of flags</title>
+        <xsl:apply-templates select="tp:flags"/>
+      </section>
+    </xsl:if>
+
+    <xsl:if test="tp:struct">
+      <section>
+        <title>Struct types</title>
+        <xsl:apply-templates select="tp:struct"/>
+      </section>
+    </xsl:if>
+
+    <xsl:if test="tp:mapping">
+      <section>
+        <title>Map types</title>
+        <xsl:apply-templates select="tp:mapping"/>
+      </section>
+    </xsl:if>
+
+    <xsl:if test="tp:external-type">
+      <section>
+        <title>Types defined elsewhere</title>
+        <glosslist>
+          <xsl:apply-templates select="tp:external-type"/>
+        </glosslist>
+      </section>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="tp:error">
+    <simplesect>
+      <title>
+        <xsl:attribute name="xml:id">
+          <xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/>
+        </xsl:attribute>
+        <literal><xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/></literal>
+      </title>
+      <xsl:apply-templates select="tp:docstring"/>
+      <xsl:apply-templates select="tp:added"/>
+      <xsl:apply-templates select="tp:changed"/>
+      <xsl:apply-templates select="tp:deprecated"/>
+    </simplesect>
+  </xsl:template>
+
+  <xsl:template match="/tp:spec/tp:copyright">
+    <!-- TODO: use <copyright> -->
+    <legalnotice>
+      <para><xsl:apply-templates mode="text"/></para>
+    </legalnotice>
+  </xsl:template>
+  <xsl:template match="/tp:spec/tp:license">
+    <!-- TODO: right tag? -->
+    <legalnotice>
+      <para>
+        <xsl:apply-templates/>
+      </para>
+    </legalnotice>
+  </xsl:template>
+
+  <xsl:template match="tp:copyright"/>
+  <xsl:template match="tp:license"/>
+
+  <xsl:template match="interface">
+
+    <refentry>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="@name"/>
+      </xsl:attribute>
+      <refmeta>
+        <refentrytitle><literal><xsl:value-of select="@name"/></literal></refentrytitle>
+      </refmeta>
+
+      <refnamediv>
+        <refdescriptor><xsl:value-of select="@name"/></refdescriptor>
+        <refname><xsl:value-of select="@name"/></refname>
+        <refpurpose><xsl:apply-templates select="tp:docstring" mode="nopara"/></refpurpose>
+      </refnamediv>
+
+      <xsl:if test="tp:added">
+        <refsection>
+          <xsl:apply-templates select="tp:added"/>
+        </refsection>
+      </xsl:if>
+      <xsl:if test="tp:changed">
+        <refsection>
+          <xsl:apply-templates select="tp:changed"/>
+        </refsection>
+      </xsl:if>
+      <xsl:if test="tp:deprecated">
+        <refsection>
+          <xsl:apply-templates select="tp:deprecated"/>
+        </refsection>
+      </xsl:if>
+
+      <xsl:if test="@tp:causes-havoc">
+        <refsection>
+          <warning>
+            <para>
+              This interface is <xsl:value-of select="@tp:causes-havoc"/>
+              and is likely to cause havoc to your API/ABI if bindings are generated.
+              Don't include it in libraries that care about compatibility.
+            </para>
+          </warning>
+        </refsection>
+      </xsl:if>
+
+      <xsl:if test="tp:requires">
+        <refsection>
+          <tip>
+            <para>Implementations of this interface must also implement:</para>
+            <itemizedlist>
+              <xsl:for-each select="tp:requires">
+                <listitem>
+                  <para>
+                    <link linkend="{@interface}">
+                      <literal><xsl:value-of select="@interface"/></literal>
+                    </link>
+                  </para>
+                </listitem>
+              </xsl:for-each>
+            </itemizedlist>
+          </tip>
+        </refsection>
+      </xsl:if>
+
+      <refsynopsisdiv>
+        <xsl:if test="method">
+          <refsect2>
+            <title>Methods</title>
+            <funcsynopsis>
+              <xsl:apply-templates select="method" mode="funcsynopsislinked"/>
+            </funcsynopsis>
+          </refsect2>
+        </xsl:if>
+        <xsl:if test="signal">
+          <refsect2>
+            <title>Signals</title>
+            <funcsynopsis>
+              <xsl:apply-templates select="signal" mode="funcsynopsislinked"/>
+            </funcsynopsis>
+          </refsect2>
+        </xsl:if>
+        <xsl:if test="property">
+          <refsect2>
+            <title>Properties</title>
+            <refsynopsisdiv>
+              <title> </title>
+              <xsl:apply-templates select="property" mode="fieldsynopsislinked"/>
+            </refsynopsisdiv>
+          </refsect2>
+        </xsl:if>
+      </refsynopsisdiv>
+
+      <xsl:if test="method">
+        <refsection>
+          <title>Methods</title>
+          <xsl:apply-templates select="method" mode="detail"/>
+        </refsection>
+      </xsl:if>
+
+      <xsl:if test="signal">
+        <refsection>
+          <title>Signals</title>
+          <xsl:apply-templates select="signal" mode="detail"/>
+        </refsection>
+      </xsl:if>
+
+      <xsl:if test="tp:property">
+        <refsection>
+          <title>Telepathy Properties</title>
+          <para>
+            Accessed using the
+            <link linkend="org.freedesktop.Telepathy.Properties">
+              <literal>org.freedesktop.Telepathy.Properties</literal>
+            </link>
+          </para>
+          <glosslist>
+            <xsl:apply-templates select="tp:property" mode="detail"/>
+          </glosslist>
+        </refsection>
+      </xsl:if>
+
+      <xsl:if test="property">
+        <refsection>
+          <title>D-Bus Properties</title>
+          <para>
+            Accessed using the org.freedesktop.DBus.Properties interface.
+          </para>
+          <xsl:apply-templates select="property" mode="detail"/>
+        </refsection>
+      </xsl:if>
+
+      <xsl:call-template name="do-types"/>
+
+    </refentry>
+
+  </xsl:template>
+
+  <xsl:template match="tp:flags">
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a tp:flags type&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@type) or @type = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @type on tp:flags type</xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <section>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="@name"/>
+      </xsl:attribute>
+      <title><literal><xsl:value-of select="@name"/></literal></title>
+      <xsl:apply-templates select="tp:docstring" />
+      <xsl:apply-templates select="tp:added"/>
+      <xsl:apply-templates select="tp:changed"/>
+      <xsl:apply-templates select="tp:deprecated"/>
+      <glosslist>
+        <xsl:variable name="value-prefix">
+          <xsl:choose>
+            <xsl:when test="@value-prefix">
+              <xsl:value-of select="@value-prefix"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="@name"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <xsl:for-each select="tp:flag">
+          <glossentry>
+            <glossterm>
+              <xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/>
+            </glossterm>
+            <glossdef>
+              <xsl:choose>
+                <xsl:when test="tp:docstring">
+                  <xsl:apply-templates select="tp:docstring" />
+                  <xsl:apply-templates select="tp:added"/>
+                  <xsl:apply-templates select="tp:changed"/>
+                  <xsl:apply-templates select="tp:deprecated"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  (Undocumented)
+                </xsl:otherwise>
+              </xsl:choose>
+            </glossdef>
+          </glossentry>
+        </xsl:for-each>
+      </glosslist>
+    </section>
+  </xsl:template>
+
+  <xsl:template match="tp:enum">
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a tp:enum type&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@type) or @type = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @type on tp:enum type</xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <section>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat('type-', @name)"/>
+      </xsl:attribute>
+      <title><literal><xsl:value-of select="@name"/></literal></title>
+      <xsl:apply-templates select="tp:docstring" />
+      <xsl:apply-templates select="tp:added"/>
+      <xsl:apply-templates select="tp:changed"/>
+      <xsl:apply-templates select="tp:deprecated"/>
+      <glosslist>
+        <xsl:variable name="value-prefix">
+          <xsl:choose>
+            <xsl:when test="@value-prefix">
+              <xsl:value-of select="@value-prefix"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="@name"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+        <xsl:for-each select="tp:enumvalue">
+          <glossentry>
+            <glossterm>
+              <xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/>
+            </glossterm>
+            <glossdef>
+              <xsl:choose>
+                <xsl:when test="tp:docstring">
+                  <xsl:apply-templates select="tp:docstring" />
+                  <xsl:apply-templates select="tp:added"/>
+                  <xsl:apply-templates select="tp:changed"/>
+                  <xsl:apply-templates select="tp:deprecated"/>
+                </xsl:when>
+                <xsl:otherwise>
+                  (Undocumented)
+                </xsl:otherwise>
+              </xsl:choose>
+            </glossdef>
+          </glossentry>
+        </xsl:for-each>
+      </glosslist>
+    </section>
+  </xsl:template>
+
+  <xsl:template match="property" mode="fieldsynopsis">
+
+    <fieldsynopsis>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat(../@name, '.', @name)"/>
+      </xsl:attribute>
+      <modifier>
+        <xsl:choose>
+          <xsl:when test="@access = 'read'">
+            <xsl:text>READ</xsl:text>
+          </xsl:when>
+          <xsl:when test="@access = 'write'">
+            <xsl:text>WRITE</xsl:text>
+          </xsl:when>
+          <xsl:when test="@access = 'readwrite'">
+            <xsl:text>READWRITE</xsl:text>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:message terminate="yes">
+              <xsl:text>ERR: unknown or missing value for </xsl:text>
+              <xsl:text>@access on property </xsl:text>
+              <xsl:value-of select="concat(../@name, '.', @name)"/>
+              <xsl:text>: '</xsl:text>
+              <xsl:value-of select="@access"/>
+              <xsl:text>'&#10;</xsl:text>
+            </xsl:message>
+          </xsl:otherwise>
+        </xsl:choose>
+      </modifier>
+      <type>
+        <xsl:call-template name="ResolveType">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </type>
+      <varname>
+        <xsl:value-of select="@name"/>
+      </varname>
+    </fieldsynopsis>
+
+  </xsl:template>
+
+  <xsl:template match="property" mode="fieldsynopsislinked">
+
+    <fieldsynopsis>
+      <modifier>
+        <xsl:choose>
+          <xsl:when test="@access = 'read'">
+            <xsl:text>READ</xsl:text>
+          </xsl:when>
+          <xsl:when test="@access = 'write'">
+            <xsl:text>WRITE</xsl:text>
+          </xsl:when>
+          <xsl:when test="@access = 'readwrite'">
+            <xsl:text>READWRITE</xsl:text>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:message terminate="yes">
+              <xsl:text>ERR: unknown or missing value for </xsl:text>
+              <xsl:text>@access on property </xsl:text>
+              <xsl:value-of select="concat(../@name, '.', @name)"/>
+              <xsl:text>: '</xsl:text>
+              <xsl:value-of select="@access"/>
+              <xsl:text>'&#10;</xsl:text>
+            </xsl:message>
+          </xsl:otherwise>
+        </xsl:choose>
+      </modifier>
+      <type>
+        <xsl:call-template name="ResolveType">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </type>
+      <varname>
+        <xsl:attribute name="xlink:href">
+          <xsl:value-of select="concat('#', ../@name, '.', @name)"/>
+        </xsl:attribute>
+        <xsl:value-of select="@name"/>
+      </varname>
+    </fieldsynopsis>
+
+  </xsl:template>
+
+  <xsl:template match="property" mode="detail">
+
+    <xsl:if test="not(parent::interface)">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: property </xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text> does not have an interface as parent&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a property of </xsl:text>
+        <xsl:value-of select="../@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@type) or @type = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @type on property </xsl:text>
+        <xsl:value-of select="concat(../@name, '.', @name)"/>
+        <xsl:text>: '</xsl:text>
+        <xsl:value-of select="@access"/>
+        <xsl:text>'&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:apply-templates select="." mode="fieldsynopsis"/>
+
+    <xsl:apply-templates select="tp:docstring"/>
+    <xsl:apply-templates select="tp:added"/>
+    <xsl:apply-templates select="tp:changed"/>
+    <xsl:apply-templates select="tp:deprecated"/>
+  </xsl:template>
+
+  <xsl:template match="tp:property" mode="detail">
+    <glossentry>
+      <glossterm>
+        <xsl:if test="@name">
+          <xsl:value-of select="@name"/> −
+        </xsl:if>
+        <xsl:value-of select="@type"/>
+      </glossterm>
+      <glossdef>
+        <xsl:apply-templates select="tp:docstring"/>
+        <xsl:apply-templates select="tp:added"/>
+        <xsl:apply-templates select="tp:changed"/>
+        <xsl:apply-templates select="tp:deprecated"/>
+      </glossdef>
+    </glossentry>
+  </xsl:template>
+
+  <xsl:template match="tp:mapping">
+    <section>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat('type-', @name)"/>
+      </xsl:attribute>
+      <title>
+        <literal><xsl:value-of select="@name"/></literal>
+      </title>
+      <xsl:apply-templates select="tp:docstring"/>
+      <xsl:if test="string(@array-name) != ''">
+        <para>
+          In bindings that need a separate name, arrays of
+          <xsl:value-of select="@name"/> should be called
+          <xsl:value-of select="@array-name"/>.
+        </para>
+      </xsl:if>
+      <section>
+        <title>Members</title>
+        <glosslist>
+          <xsl:apply-templates select="tp:member" mode="description"/>
+        </glosslist>
+      </section>
+    </section>
+  </xsl:template>
+
+  <xsl:template match="tp:docstring" mode="in-index"/>
+
+  <xsl:template match="tp:simple-type | tp:enum | tp:flags | tp:external-type"
+    mode="in-index">
+    − <xsl:value-of select="@type"/>
+  </xsl:template>
+
+  <xsl:template match="tp:simple-type">
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a tp:simple-type&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@type) or @type = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @type on tp:simple-type</xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <section>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat('type-', @name)"/>
+      </xsl:attribute>
+      <title>
+        <literal><xsl:value-of select="@name"/> − <xsl:value-of select="@type"/></literal>
+      </title>
+      <para>
+        <xsl:apply-templates select="tp:docstring"/>
+        <xsl:apply-templates select="tp:added"/>
+        <xsl:apply-templates select="tp:changed"/>
+        <xsl:apply-templates select="tp:deprecated"/>
+      </para>
+    </section>
+  </xsl:template>
+
+  <xsl:template match="tp:external-type">
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a tp:external-type&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@type) or @type = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @type on tp:external-type</xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <glossentry>
+      <glossterm>
+        <xsl:attribute name="xml:id">
+          <xsl:value-of select="concat('type-', @name)"/>
+        </xsl:attribute>
+        <xsl:value-of select="@name"/> − <xsl:value-of select="@type"/>
+      </glossterm>
+      <glossdef>Defined by: <xsl:value-of select="@from"/></glossdef>
+    </glossentry>
+  </xsl:template>
+
+  <xsl:template match="tp:struct" mode="in-index">
+    − ( <xsl:for-each select="tp:member">
+          <xsl:value-of select="@type"/>
+          <xsl:if test="position() != last()">, </xsl:if>
+        </xsl:for-each> )
+  </xsl:template>
+
+  <xsl:template match="tp:mapping" mode="in-index">
+    − a{ <xsl:for-each select="tp:member">
+          <xsl:value-of select="@type"/>
+          <xsl:if test="position() != last()"> &#x2192; </xsl:if>
+        </xsl:for-each> }
+  </xsl:template>
+
+  <xsl:template match="tp:struct">
+    <section>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat('type-', @name)"/>
+      </xsl:attribute>
+      <title>
+        <literal>
+          <xsl:value-of select="@name"/>
+        </literal>
+      </title>
+      <xsl:apply-templates select="tp:docstring"/>
+      <xsl:apply-templates select="tp:added"/>
+      <xsl:apply-templates select="tp:changed"/>
+      <xsl:apply-templates select="tp:deprecated"/>
+      <xsl:choose>
+        <xsl:when test="string(@array-name) != ''">
+          <para>In bindings that need a separate name, arrays of
+            <xsl:value-of select="@name"/> should be called
+            <xsl:value-of select="@array-name"/>.</para>
+        </xsl:when>
+        <xsl:otherwise>
+          <para>Arrays of <xsl:value-of select="@name"/> don't generally
+            make sense.</para>
+        </xsl:otherwise>
+      </xsl:choose>
+      <classsynopsis>
+        <ooclass>
+          <modifier>struct</modifier>
+          <classname><xsl:value-of select="@name"/></classname>
+        </ooclass>
+        <xsl:apply-templates select="tp:member" mode="fieldsynopsis"/>
+      </classsynopsis>
+      <glosslist>
+        <xsl:apply-templates select="tp:member" mode="description"/>
+      </glosslist>
+    </section>
+  </xsl:template>
+
+  <xsl:template match="arg" mode="paramdef">
+    <paramdef>
+      <xsl:call-template name="direction">
+        <xsl:with-param name="indirection" select="@direction"/>
+      </xsl:call-template>
+      <xsl:text> </xsl:text>
+      <type>
+        <xsl:call-template name="ResolveType">
+          <xsl:with-param name="node" select="."/>
+        </xsl:call-template>
+      </type>
+      <xsl:text> </xsl:text>
+      <parameter><xsl:value-of select="@name"/></parameter>
+    </paramdef>
+  </xsl:template>
+
+  <xsl:template match="arg" mode="paramtable">
+    <glossentry>
+      <glossterm><literal><xsl:value-of select="@name"/></literal></glossterm>
+      <glossdef>
+        <para><xsl:apply-templates select="tp:docstring" mode="nopara"/></para>
+      </glossdef>
+    </glossentry>
+  </xsl:template>
+
+  <xsl:template match="method|signal" mode="funcsynopsis">
+    <funcsynopsis>
+      <funcprototype>
+        <funcdef>
+          <function>
+            <xsl:value-of select="@name"/>
+          </function>
+        </funcdef>
+        <xsl:choose>
+          <xsl:when test="arg">
+            <xsl:apply-templates select="arg" mode="paramdef"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <void/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </funcprototype>
+    </funcsynopsis>
+  </xsl:template>
+
+  <xsl:template match="method|signal" mode="funcsynopsislinked">
+    <funcprototype>
+      <funcdef>
+        <function linkend="{concat(parent::interface//@name, '.', @name)}">
+          <xsl:value-of select="@name"/>
+        </function>
+      </funcdef>
+      <xsl:choose>
+        <xsl:when test="arg">
+          <xsl:apply-templates select="arg" mode="paramdef"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <void/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </funcprototype>
+  </xsl:template>
+
+  <xsl:template match="method" mode="detail">
+
+    <xsl:if test="not(parent::interface)">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: method </xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text> does not have an interface as parent&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a method of </xsl:text>
+        <xsl:value-of select="../@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:for-each select="arg">
+      <xsl:if test="not(@type) or @type = ''">
+        <xsl:message terminate="yes">
+          <xsl:text>ERR: an arg of method </xsl:text>
+          <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+          <xsl:text> has no type</xsl:text>
+        </xsl:message>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="@direction='in'">
+          <xsl:if test="not(@name) or @name = ''">
+            <xsl:message terminate="yes">
+              <xsl:text>ERR: an 'in' arg of method </xsl:text>
+              <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+              <xsl:text> has no name</xsl:text>
+            </xsl:message>
+          </xsl:if>
+        </xsl:when>
+        <xsl:when test="@direction='out'">
+          <xsl:if test="not(@name) or @name = ''">
+            <xsl:message terminate="no">
+              <xsl:text>INFO: an 'out' arg of method </xsl:text>
+              <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+              <xsl:text> has no name</xsl:text>
+            </xsl:message>
+          </xsl:if>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:message terminate="yes">
+            <xsl:text>ERR: an arg of method </xsl:text>
+            <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+            <xsl:text> has direction neither 'in' nor 'out'</xsl:text>
+          </xsl:message>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+
+    <refsection>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat(../@name, concat('.', @name))"/>
+      </xsl:attribute>
+      <title>
+        <literal><xsl:value-of select="concat(../@name, concat('.', @name))"/></literal>
+      </title>
+      <xsl:apply-templates select="." mode="funcsynopsis"/>
+      <xsl:apply-templates select="tp:docstring"/>
+      <xsl:apply-templates select="tp:added"/>
+      <xsl:apply-templates select="tp:changed"/>
+      <xsl:apply-templates select="tp:deprecated"/>
+
+        <xsl:if test="arg">
+          <glosslist>
+            <xsl:apply-templates select="arg" mode="paramtable"/>
+          </glosslist>
+        </xsl:if>
+
+        <xsl:if test="tp:possible-errors">
+          <formalpara>
+            <title>Possible errors</title>
+            <para>
+              <glosslist>
+                <xsl:apply-templates select="tp:possible-errors/tp:error"/>
+              </glosslist>
+            </para>
+          </formalpara>
+        </xsl:if>
+    </refsection>
+  </xsl:template>
+
+  <xsl:template name="tp-type">
+    <xsl:param name="tp-type"/>
+    <xsl:param name="type"/>
+
+    <xsl:variable name="single-type">
+      <xsl:choose>
+        <xsl:when test="contains($tp-type, '[]')">
+          <xsl:value-of select="substring-before($tp-type, '[]')"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$tp-type"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+
+    <xsl:variable name="type-of-tp-type">
+      <xsl:if test="contains($tp-type, '[]')">
+        <!-- one 'a', plus one for each [ after the [], and delete all ] -->
+        <xsl:value-of select="concat('a',
+          translate(substring-after($tp-type, '[]'), '[]', 'a'))"/>
+      </xsl:if>
+
+      <xsl:choose>
+        <xsl:when test="//tp:simple-type[@name=$single-type]">
+          <xsl:value-of select="string(//tp:simple-type[@name=$single-type]/@type)"/>
+        </xsl:when>
+        <xsl:when test="//tp:struct[@name=$single-type]">
+          <xsl:text>(</xsl:text>
+          <xsl:for-each select="//tp:struct[@name=$single-type]/tp:member">
+            <xsl:value-of select="@type"/>
+          </xsl:for-each>
+          <xsl:text>)</xsl:text>
+        </xsl:when>
+        <xsl:when test="//tp:enum[@name=$single-type]">
+          <xsl:value-of select="string(//tp:enum[@name=$single-type]/@type)"/>
+        </xsl:when>
+        <xsl:when test="//tp:flags[@name=$single-type]">
+          <xsl:value-of select="string(//tp:flags[@name=$single-type]/@type)"/>
+        </xsl:when>
+        <xsl:when test="//tp:mapping[@name=$single-type]">
+          <xsl:text>a{</xsl:text>
+          <xsl:for-each select="//tp:mapping[@name=$single-type]/tp:member">
+            <xsl:value-of select="@type"/>
+          </xsl:for-each>
+          <xsl:text>}</xsl:text>
+        </xsl:when>
+        <xsl:when test="//tp:external-type[@name=$single-type]">
+          <xsl:value-of select="string(//tp:external-type[@name=$single-type]/@type)"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:message terminate="yes">
+            <xsl:text>ERR: Unable to find type '</xsl:text>
+            <xsl:value-of select="$tp-type"/>
+            <xsl:text>'&#10;</xsl:text>
+          </xsl:message>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+
+    <xsl:if test="string($type) != '' and
+      string($type-of-tp-type) != string($type)">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: tp:type '</xsl:text>
+        <xsl:value-of select="$tp-type"/>
+        <xsl:text>' has D-Bus type '</xsl:text>
+        <xsl:value-of select="$type-of-tp-type"/>
+        <xsl:text>' but has been used with type='</xsl:text>
+        <xsl:value-of select="$type"/>
+        <xsl:text>'&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <link linkend="type-{$single-type}">
+      <literal><xsl:value-of select="concat('type-', $single-type)"/></literal>
+    </link>
+
+  </xsl:template>
+
+  <xsl:template name="parenthesized-tp-type">
+    <xsl:if test="@tp:type">
+      <xsl:text> (</xsl:text>
+      <xsl:call-template name="tp-type">
+        <xsl:with-param name="tp-type" select="@tp:type"/>
+        <xsl:with-param name="type" select="@type"/>
+      </xsl:call-template>
+      <xsl:text>)</xsl:text>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="tp:member" mode="fieldsynopsis">
+    <xsl:variable name="type">
+      <xsl:call-template name="ResolveType">
+        <xsl:with-param name="node" select="."/>
+      </xsl:call-template>
+    </xsl:variable>
+    <fieldsynopsis>
+      <type><xsl:value-of select="normalize-space($type)"/></type>
+      <varname><xsl:value-of select="@name"/></varname>
+    </fieldsynopsis>
+  </xsl:template>
+
+  <xsl:template match="tp:member" mode="description">
+    <glossentry>
+      <glossterm>
+        <varname><xsl:value-of select="@name"/></varname>
+      </glossterm>
+      <glossdef>
+        <xsl:choose>
+          <xsl:when test="tp:docstring">
+            <xsl:apply-templates select="tp:docstring" />
+          </xsl:when>
+          <xsl:otherwise>
+            <!-- emphasize -->
+            (undocumented)
+          </xsl:otherwise>
+        </xsl:choose>
+      </glossdef>
+    </glossentry>
+  </xsl:template>
+
+  <xsl:template match="tp:possible-errors/tp:error">
+    <glossentry>
+      <glossterm>
+        <xsl:value-of select="@name"/>
+      </glossterm>
+      <glossdef>
+        <xsl:variable name="name" select="@name"/>
+        <xsl:choose>
+          <xsl:when test="tp:docstring">
+            <xsl:apply-templates select="tp:docstring"/>
+          </xsl:when>
+          <xsl:when test="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring">
+            <xsl:apply-templates select="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"/> <!-- TODO: emphasize -->(generic description)
+          </xsl:when>
+          <xsl:otherwise>
+            (Undocumented.)
+          </xsl:otherwise>
+        </xsl:choose>
+      </glossdef>
+    </glossentry>
+  </xsl:template>
+
+  <xsl:template match="signal" mode="detail">
+
+    <xsl:if test="not(parent::interface)">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: signal </xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text> does not have an interface as parent&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:if test="not(@name) or @name = ''">
+      <xsl:message terminate="yes">
+        <xsl:text>ERR: missing @name on a signal of </xsl:text>
+        <xsl:value-of select="../@name"/>
+        <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+
+    <xsl:for-each select="arg">
+      <xsl:if test="not(@type) or @type = ''">
+        <xsl:message terminate="yes">
+          <xsl:text>ERR: an arg of signal </xsl:text>
+          <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+          <xsl:text> has no type</xsl:text>
+        </xsl:message>
+      </xsl:if>
+      <xsl:if test="not(@name) or @name = ''">
+        <xsl:message terminate="yes">
+          <xsl:text>ERR: an arg of signal </xsl:text>
+          <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+          <xsl:text> has no name</xsl:text>
+        </xsl:message>
+      </xsl:if>
+      <xsl:choose>
+        <xsl:when test="not(@direction)"/>
+        <xsl:when test="@direction='in'"/>
+        <!-- This doesn't work with the DTD (see comment in DTD)
+        <xsl:when test="@direction='in'">
+          <xsl:message terminate="no">
+            <xsl:text>INFO: an arg of signal </xsl:text>
+            <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+            <xsl:text> has unnecessary direction 'in'</xsl:text>
+          </xsl:message>
+        </xsl:when>
+        -->
+        <xsl:otherwise>
+          <xsl:message terminate="yes">
+            <xsl:text>ERR: an arg of signal </xsl:text>
+            <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+            <xsl:text> has direction other than 'in'</xsl:text>
+          </xsl:message>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:for-each>
+
+    <refsection>
+      <xsl:attribute name="xml:id">
+        <xsl:value-of select="concat(../@name, concat('.', @name))"/>
+      </xsl:attribute>
+      <title>
+        <literal><xsl:value-of select="concat(../@name, concat('.', @name))"/></literal>
+      </title>
+      <xsl:apply-templates select="." mode="funcsynopsis"/>
+      <xsl:apply-templates select="tp:docstring"/>
+      <xsl:apply-templates select="tp:added"/>
+      <xsl:apply-templates select="tp:changed"/>
+      <xsl:apply-templates select="tp:deprecated"/>
+
+      <xsl:if test="arg">
+        <glosslist>
+          <xsl:apply-templates select="arg" mode="paramtable"/>
+        </glosslist>
+      </xsl:if>
+    </refsection>
+  </xsl:template>
+
+  <xsl:template match="/tp:spec">
+    <book xmlns="http://docbook.org/ns/docbook" version="5.0">
+      <bookinfo>
+        <title><xsl:value-of select="tp:title"/></title>
+        <xsl:apply-templates select="tp:copyright"/>
+        <xsl:apply-templates select="tp:license"/>
+        <xsl:if test="tp:docstring">
+          <abstract>
+            <xsl:apply-templates select="tp:docstring"/>
+          </abstract>
+        </xsl:if>
+        <!-- TODO: Version
+        <xsl:if test="tp:version">
+          <xsl:text> version </xsl:text>
+          <xsl:value-of select="tp:version"/>
+        </xsl:if> -->
+      </bookinfo>
+      <chapter>
+        <xsl:attribute name="xml:id">interfaces</xsl:attribute>
+        <title>Interfaces</title>
+        <xsl:apply-templates select="//node"/>
+      </chapter>
+      <xsl:call-template name="generic-types"/>
+      <xsl:if test="tp:errors">
+        <chapter>
+            <xsl:attribute name="xml:id">errors</xsl:attribute>
+            <xsl:apply-templates select="tp:errors"/>
+        </chapter>
+      </xsl:if>
+    </book>
+  </xsl:template>
+
+  <xsl:template match="node">
+      <xsl:apply-templates />
+  </xsl:template>
+
+  <xsl:template match="text()">
+    <xsl:if test="normalize-space(.) != ''">
+      <xsl:message terminate="yes">
+        <xsl:text>Stray text: {{{</xsl:text>
+        <xsl:value-of select="." />
+        <xsl:text>}}}&#10;</xsl:text>
+      </xsl:message>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="*">
+      <xsl:message terminate="yes">
+         <xsl:text>Unrecognised element: {</xsl:text>
+         <xsl:value-of select="namespace-uri(.)" />
+         <xsl:text>}</xsl:text>
+         <xsl:value-of select="local-name(.)" />
+         <xsl:text>&#10;</xsl:text>
+      </xsl:message>
+  </xsl:template>
+</xsl:stylesheet>
+
+<!-- vim:set sw=2 sts=2 et: -->
diff --git a/idle-inhibit/tools/spec-to-introspect.xsl b/idle-inhibit/tools/spec-to-introspect.xsl
new file mode 100644
index 0000000..2468237
--- /dev/null
+++ b/idle-inhibit/tools/spec-to-introspect.xsl
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+        exclude-result-prefixes="tp">
+
+<!--
+    Telepathy D-Bus Introspection to EggDBus Introspection format translator.
+
+    Copyright 2009  Michael Leupold <lemma@confuego.org>
+
+    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
+-->
+
+<!--
+    TODO:
+     - Enable conversion of dictionary element types (eg. "{ss}") and
+       struct types (eg. "(sayay)")
+     - unhandled: tp:simple-type, tp:enum, tp:flags, tp:external-type
+     - tp:docstring may contain XHTML which this template doesn't handle
+-->
+
+    <xsl:include href="resolve-type.xsl"/>
+
+    <!-- main template -->
+    <xsl:template match="tp:spec">
+        <node>
+            <xsl:apply-templates select="tp:errors"/>
+            <xsl:apply-templates select="tp:struct"/>
+            <!-- TODO: <xsl:apply-templates select="tp:mapping"/> -->
+            <xsl:apply-templates select="node/interface"/>
+        </node>
+    </xsl:template>
+
+    <!-- handle most of the D-Bus introspection elements -->
+    <xsl:template match="interface|annotation|method|signal">
+        <xsl:copy>
+            <xsl:for-each select="@*">
+                <xsl:if test="not(starts-with(name(), 'tp:'))">
+                    <xsl:copy/>
+                </xsl:if>
+            </xsl:for-each>
+            <xsl:apply-templates/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- handle the arg and property D-Bus introspection elements.
+         They get special handling because they may contain a tp:type
+         attribute -->
+    <xsl:template match="arg|property">
+        <xsl:copy>
+            <xsl:for-each select="@*">
+                <xsl:if test="not(starts-with(name(), 'tp:'))">
+                    <xsl:copy/>
+                </xsl:if>
+            </xsl:for-each>
+            <xsl:for-each select="@tp:type">
+                <xsl:variable name="type">
+                    <xsl:call-template name="TpType">
+                        <xsl:with-param name="type" select="."/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <annotation name="org.gtk.EggDBus.Type">
+                    <xsl:attribute name="value">
+                        <xsl:value-of select="translate(translate($type, ' ', ''), '&#xa;', '')"/>
+                    </xsl:attribute>
+                </annotation>
+            </xsl:for-each>
+            <xsl:apply-templates/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- tp:docstring to org.gtk.EggDBus.DocString -->
+    <xsl:template match="tp:docstring">
+        <annotation name="org.gtk.EggDBus.DocString">
+            <xsl:attribute name="value">
+                <xsl:value-of select="normalize-space(text())"/>
+            </xsl:attribute>
+        </annotation>
+    </xsl:template>
+
+    <!-- tp:errors to org.gtk.EggDBus.DeclareErrorDomain -->
+    <xsl:template match="tp:errors">
+        <annotation value="Error" name="org.gtk.EggDBus.DeclareErrorDomain">
+            <xsl:apply-templates select="tp:docstring"/>
+            <xsl:apply-templates select="tp:error"/>
+        </annotation>
+    </xsl:template>
+
+    <!-- tp:error to org.gtk.EggDBus.ErrorDomain.Member -->
+    <xsl:template match="tp:error">
+        <annotation name="org.gtk.EggDBus.ErrorDomain.Member">
+            <xsl:attribute name="value">
+                <xsl:value-of select="concat(../@namespace, '.', @name)"/>
+            </xsl:attribute>
+            <xsl:apply-templates select="tp:docstring"/>
+        </annotation>
+    </xsl:template>
+
+    <!-- tp:struct to org.gtk.EggDBus.DeclareStruct -->
+    <xsl:template match="tp:struct">
+        <annotation name="org.gtk.EggDBus.DeclareStruct">
+            <xsl:attribute name="value">
+                <xsl:value-of select="@name"/>
+            </xsl:attribute>
+            <xsl:apply-templates select="tp:docstring"/>
+            <xsl:apply-templates select="tp:member"/>
+        </annotation>
+    </xsl:template>
+
+    <!-- tp:member to org.gtk.EggDBus.Struct.Member -->
+    <xsl:template match="tp:member">
+        <xsl:variable name="type">
+            <xsl:call-template name="ResolveType">
+                <xsl:with-param name="node" select="."/>
+            </xsl:call-template>
+        </xsl:variable>
+        <annotation name="org.gtk.EggDBus.Struct.Member">
+            <xsl:attribute name="value">
+                <xsl:value-of select="concat(normalize-space($type), ':', @name)"/>
+            </xsl:attribute>
+            <xsl:apply-templates select="tp:docstring"/>
+        </annotation>
+    </xsl:template>
+
+    <xsl:template match="text()"/>
+
+    <xsl:output method="xml" indent="yes" encoding="UTF-8"
+                omit-xml-declaration="no"
+                doctype-system="http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
+                doctype-public="-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"/>
+
+</xsl:stylesheet>
-- 
1.8.0.1

