diff options
author | Jonny Lamb <jonny.lamb@collabora.co.uk> | 2010-10-28 13:47:27 +0100 |
---|---|---|
committer | Jonny Lamb <jonny.lamb@collabora.co.uk> | 2010-10-28 13:47:27 +0100 |
commit | f1416d10f69c45f68cf66e7b82bc2e23baa08128 (patch) | |
tree | 52aae18a614d9ede8412705f4e31057436778a7c |
initial commit
Signed-off-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
48 files changed, 9236 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4cd12fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +*.o +*.lo +*.la +*.py[co] +*.[oa] +*.l[oa] +.deps +.libs +/*.cdbs-config_list +/*.cdbs-orig +/aclocal.m4 +/autom4te.cache +/config.guess +/config.h +/config.h.in +/config.log +/config.status +/config.sub +/configure +/depcomp +/install-sh +/libtool +/ltmain.sh +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/missing +/telepathy-yell/*.pc +/telepathy-yell/extensions.html +/stamp-h1 +Makefile +Makefile.in +_gen +*~
\ No newline at end of file @@ -0,0 +1,523 @@ +Copyright © 2008–2009 Collabora Ltd. <http://www.collabora.co.uk/> +Copyright © 2008–2009 Nokia Corporation + +librtcom-telepathy-glib is largely licensed under the GNU Lesser General Public +License, as published by the Free Software Foundation and reproduced below: +either version 2.1 of the License, or (at your option) any later version. + +Certain tools, taken from telepathy-glib, are instead covered by a simple +permissive license: + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. + +------------------------------------------------------------------------ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..f62c9a3 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = m4 tools telepathy-yell + +EXTRA_DIST = \ + autogen.sh diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..7f08e9b --- /dev/null +++ b/autogen.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -e + +if test -n "$AUTOMAKE"; then + : # don't override an explicit user request +elif automake-1.9 --version >/dev/null 2>/dev/null && \ + aclocal-1.9 --version >/dev/null 2>/dev/null; then + # If we have automake-1.9, use it. This helps to ensure that our build + # system doesn't accidentally grow automake-1.10 dependencies. + AUTOMAKE=automake-1.9 + export AUTOMAKE + ACLOCAL=aclocal-1.9 + export ACLOCAL +fi + +autoreconf -i -f + +run_configure=true +for arg in $*; do + case $arg in + --no-configure) + run_configure=false + ;; + *) + ;; + esac +done + +if test $run_configure = true; then + ./configure "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..afa607b --- /dev/null +++ b/configure.ac @@ -0,0 +1,123 @@ +AC_PREREQ([2.59]) + +# Making releases: +# set the new version number: +# odd minor -> development series +# even minor -> stable series +# increment micro for each release within a series +# set nano_version to 0 +# make the release, tag it +# set nano_version to 1 + +m4_define([tp_yell_major_version], [0]) +m4_define([tp_yell_minor_version], [0]) +m4_define([tp_yell_micro_version], [0]) +m4_define([tp_yell_nano_version], [1]) + +# Some magic +m4_define([tp_yell_base_version], + [tp_yell_major_version.tp_yell_minor_version.tp_yell_micro_version]) +m4_define([tp_yell_version], + [m4_if(tp_yell_nano_version, 0, [tp_yell_base_version], + [tp_yell_base_version].[tp_yell_nano_version])]) + +AC_INIT([telepathy-yell], [tp_yell_version], + [mailto:telepathy@lists.freedesktop.org]) + +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([1.9 -Wall -Wno-portability foreign]) +AM_CONFIG_HEADER(config.h) + +AM_SILENT_RULES + +dnl check for tools +AC_PROG_CC +AC_PROG_CC_STDC +AC_PROG_INSTALL +AC_PROG_LIBTOOL +AM_PROG_LIBTOOL +AM_PROG_MKDIR_P + +dnl optimizations, etc. +COMPILER_OPTIMISATIONS +COMPILER_COVERAGE +LINKER_OPTIMISATIONS +LINKER_VERSION_SCRIPT + +dnl decide error flags +ifelse(tp_yell_nano_version, 0, + [ official_release=yes ], + [ official_release=no ]) + +TP_COMPILER_WARNINGS([ERROR_CFLAGS], [test "x$official_release" = xno], + [all \ + extra \ + declaration-after-statement \ + shadow \ + strict-prototypes \ + missing-prototypes \ + sign-compare \ + nested-externs \ + pointer-arith \ + format-security \ + init-self], + [missing-field-initializers \ + unused-parameter]) +AC_SUBST([ERROR_CFLAGS]) + +ifelse(tp_yell_nano_version, 0, + [ # version x.y.z - disable coding style checks by default +AC_ARG_ENABLE(coding-style-checks, + AC_HELP_STRING([--enable-coding-style-checks], + [check coding style using grep]), + [ENABLE_CODING_STYLE_CHECKS=$enableval], [ENABLE_CODING_STYLE_CHECKS=no] ) + ], + [ # version x.y.z.1 - enable coding style checks by default +AC_ARG_ENABLE(coding-style-checks, + AC_HELP_STRING([--disable-coding-style-checks], + [don't check coding style using grep]), + [ENABLE_CODING_STYLE_CHECKS=$enableval], [ENABLE_CODING_STYLE_CHECKS=yes]) + ]) + +AC_SUBST([ENABLE_CODING_STYLE_CHECKS]) + +dnl Check for Glib +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.25.16, gobject-2.0 >= 2.25.16]) + +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` +AC_SUBST(GLIB_GENMARSHAL) + +GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` +AC_SUBST(GLIB_MKENUMS) + +dnl Check for D-Bus +PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.95, dbus-glib-1 >= 0.82]) + +AC_SUBST(DBUS_CFLAGS) +AC_SUBST(DBUS_LIBS) + +dnl Check for D-Bus +PKG_CHECK_MODULES(TP_GLIB, [telepathy-glib >= 0.7.18]) + +AC_SUBST(TP_GLIB_CFLAGS) +AC_SUBST(TP_GLIB_LIBS) + +dnl Check for code generation tools +XSLTPROC= +AC_CHECK_PROGS([XSLTPROC], [xsltproc]) +if test -z "$XSLTPROC"; then + AC_MSG_ERROR([xsltproc (from the libxslt source package) is required]) +fi +AM_PATH_PYTHON([2.5]) + +AC_OUTPUT( Makefile \ + telepathy-yell/Makefile \ + telepathy-yell/telepathy-yell.pc \ + telepathy-yell/telepathy-yell-uninstalled.pc \ + tools/Makefile \ + m4/Makefile +) diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 0000000..677ce8b --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1,6 @@ +EXTRA_DIST = \ +as-compiler-flag.m4 \ +compiler.m4 \ +linker.m4 \ +tp-compiler-flag.m4 \ +tp-compiler-warning.m4 diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4 new file mode 100644 index 0000000..605708a --- /dev/null +++ b/m4/as-compiler-flag.m4 @@ -0,0 +1,33 @@ +dnl as-compiler-flag.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flags + +dnl David Schleef <ds@schleef.org> + +dnl $Id: as-compiler-flag.m4,v 1.1 2005/06/18 18:02:46 burgerman Exp $ + +dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + diff --git a/m4/compiler.m4 b/m4/compiler.m4 new file mode 100644 index 0000000..5aff5d8 --- /dev/null +++ b/m4/compiler.m4 @@ -0,0 +1,67 @@ +# compiler.m4 - autoconf macros for compiler settings +# +# Copyright © 2005 Scott James Remnant <scott@netsplit.com>. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# COMPILER_WARNINGS +# ---------------------- +# Add configure option to enable additional compiler warnings and treat +# them as errors. +AC_DEFUN([COMPILER_WARNINGS], +[AC_ARG_ENABLE(compiler-warnings, + AS_HELP_STRING([--enable-compiler-warnings], + [Enable additional compiler warnings]), +[if test "x$enable_compiler_warnings" = "xyes"; then + if test "x$GCC" = "xyes"; then + CFLAGS="-Wall -Werror $CFLAGS" + fi + if test "x$GXX" = "xyes"; then + CXXFLAGS="-Wall -Werror $CXXFLAGS" + fi +fi])dnl +])# COMPILER_WARNINGS + +# COMPILER_OPTIMISATIONS +# --------------------------- +# Add configure option to disable optimisations. +AC_DEFUN([COMPILER_OPTIMISATIONS], +[AC_ARG_ENABLE(compiler-optimisations, + AS_HELP_STRING([--disable-compiler-optimisations], + [Disable compiler optimisations]), +[if test "x$enable_compiler_optimisations" = "xno"; then + [CFLAGS=`echo "$CFLAGS" | sed -e "s/ -O[1-9]*\b/ -O0/g"`] +fi])dnl +])# COMPILER_OPTIMISATIONS + +# COMPILER_COVERAGE +# ---------------------- +# Add configure option to enable coverage data. +AC_DEFUN([COMPILER_COVERAGE], +[AC_ARG_ENABLE(compiler-coverage, + AS_HELP_STRING([--enable-compiler-coverage], + [Enable generation of coverage data]), +[if test "x$enable_compiler_coverage" = "xyes"; then + if test "x$GCC" = "xyes"; then + CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" + fi +fi])dnl +])# COMPILER_COVERAGE diff --git a/m4/linker.m4 b/m4/linker.m4 new file mode 100644 index 0000000..8093cb8 --- /dev/null +++ b/m4/linker.m4 @@ -0,0 +1,73 @@ +# linker.m4 - autoconf macros for linker settings +# +# Copyright © 2005 Scott James Remnant <scott@netsplit.com>. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# LINKER_OPTIMISATIONS +# -------------------- +# Add configure option to disable linker optimisations. +AC_DEFUN([LINKER_OPTIMISATIONS], +[AC_ARG_ENABLE(linker-optimisations, + AS_HELP_STRING([--disable-linker-optimisations], + [Disable linker optimisations]), +[if test "x$enable_linker_optimisations" = "xno"; then + [LDFLAGS=`echo "$LDFLAGS" | sed -e "s/ -Wl,-O[0-9]*\b//g"`] +else + [LDFLAGS="$LDFLAGS -Wl,-O1"] +fi], [LDFLAGS="$LDFLAGS -Wl,-O1"])dnl +])# LINKER_OPTIMISATIONS + +# LINKER_VERSION_SCRIPT +# -------------------------- +# Detect whether the linker supports version scripts +AC_DEFUN([LINKER_VERSION_SCRIPT], +[AC_MSG_CHECKING([for linker version script argument]) +for aranha_try_arg in "-Wl,--version-script"; do + aranha_old_libs="$LIBS" + LIBS="$LIBS $aranha_try_arg=conftest.ver" + + cat >conftest.ver <<EOF +TEST { + global: + *; +}; +EOF + + AC_TRY_LINK([], [], [ + rm -f conftest.ver + LIBS="$aranha_old_libs" + + AC_MSG_RESULT([$aranha_try_arg]) + AC_SUBST(VERSION_SCRIPT_ARG, [$aranha_try_arg]) + break + ]) + + rm -f conftest.ver + LIBS="$aranha_old_libs" +done + +AM_CONDITIONAL(HAVE_VERSION_SCRIPT_ARG, [test -n "$VERSION_SCRIPT_ARG"]) +if test -z "$VERSION_SCRIPT_ARG"; then + AC_MSG_RESULT([unknown]) +fi +])dnl +])# LINKER_VERSION_SCRIPT diff --git a/m4/tp-compiler-flag.m4 b/m4/tp-compiler-flag.m4 new file mode 100644 index 0000000..fc05e9e --- /dev/null +++ b/m4/tp-compiler-flag.m4 @@ -0,0 +1,36 @@ +dnl A version of AS_COMPILER_FLAG that supports both C and C++. +dnl Based on: + +dnl as-compiler-flag.m4 0.1.0 +dnl autostars m4 macro for detection of compiler flags +dnl David Schleef <ds@schleef.org> +dnl $Id: as-compiler-flag.m4,v 1.1 2005/06/18 18:02:46 burgerman Exp $ + +dnl TP_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS and CXXFLAGS. +dnl +dnl Runs ACTION-IF-ACCEPTED if the compiler for the currently selected +dnl AC_LANG can compile with the flags, and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([TP_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + save_CXXFLAGS="$CXXFLAGS" + CFLAGS="$CFLAGS $1" + CXXFLAGS="$CXXFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + CXXFLAGS="$save_CXXFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) diff --git a/m4/tp-compiler-warnings.m4 b/m4/tp-compiler-warnings.m4 new file mode 100644 index 0000000..fab5dc8 --- /dev/null +++ b/m4/tp-compiler-warnings.m4 @@ -0,0 +1,40 @@ +dnl TP_COMPILER_WARNINGS(VARIABLE, WERROR_BY_DEFAULT, DESIRABLE, UNDESIRABLE) +dnl $1 (VARIABLE): the variable to put flags into +dnl $2 (WERROR_BY_DEFAULT): a command returning true if -Werror should be the +dnl default +dnl $3 (DESIRABLE): warning flags we want (e.g. all extra shadow) +dnl $4 (UNDESIRABLE): warning flags we don't want (e.g. +dnl missing-field-initializers unused-parameter) +AC_DEFUN([TP_COMPILER_WARNINGS], +[ + AC_REQUIRE([AC_ARG_ENABLE])dnl + AC_REQUIRE([AC_HELP_STRING])dnl + AC_REQUIRE([TP_COMPILER_FLAG])dnl + + tp_warnings="" + for tp_flag in $3; do + TP_COMPILER_FLAG([-W$tp_flag], [tp_warnings="$tp_warnings -W$tp_flag"]) + done + + tp_error_flags="-Werror" + TP_COMPILER_FLAG([-Werror], [tp_werror=yes], [tp_werror=no]) + + for tp_flag in $4; do + TP_COMPILER_FLAG([-Wno-$tp_flag], + [tp_warnings="$tp_warnings -Wno-$tp_flag"]) + TP_COMPILER_FLAG([-Wno-error=$tp_flag], + [tp_error_flags="$tp_error_flags -Wno-error=$tp_flag"], [tp_werror=no]) + done + + AC_ARG_ENABLE([Werror], + AC_HELP_STRING([--disable-Werror], + [compile without -Werror (normally enabled in development builds)]), + tp_werror=$enableval, :) + + if test "x$tp_werror" = xyes && $2; then + $1="$tp_warnings $tp_error_flags" + else + $1="$tp_warnings" + fi + +]) diff --git a/telepathy-yell/Call_Content.xml b/telepathy-yell/Call_Content.xml new file mode 100644 index 0000000..82d4044 --- /dev/null +++ b/telepathy-yell/Call_Content.xml @@ -0,0 +1,282 @@ +<?xml version="1.0" ?> +<node name="/Call_Content" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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 <tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref>. For + example, in an audio/video call there would be one audio content + and one video content. Each content has one or more <tp:dbus-ref + namespace="ofdT.Call">Stream.DRAFT</tp:dbus-ref> objects which + represent the actual transport to one or more remote contacts. + </tp:docstring> + + <tp:enum name="Content_Removal_Reason" type="u"> + <tp:added version="0.21.2"/> + <tp:docstring> + A representation of the reason for a content to be removed, + which may be used by simple clients, or used as a fallback + when the DBus_Reason is not understood. This enum will be + extended with future reasons as and when appropriate, so + clients SHOULD keep up to date with its values, but also be + happy to fallback to the Unknown value when an unknown value + is encountered. + </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 local user requests that this content is removed + from the call.</p> + </tp:docstring> + </tp:enumvalue> + + <tp:enumvalue suffix="Error" value="2"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>There is an error with the content which means that it + has to be removed from the call.</p> + </tp:docstring> + </tp:enumvalue> + + <tp:enumvalue suffix="Unsupported" value="3"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Some aspect of the content is unsupported so has to be + removed from the call.</p> + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + <method name="Remove" tp:name-for-bindings="Remove"> + <tp:changed version="0.21.2">previously there were no + arguments</tp:changed> + <tp:docstring> + Remove the content from the call. + </tp:docstring> + + <arg direction="in" name="Reason" type="u" + tp:type="Content_Removal_Reason"> + <tp:docstring> + A generic hangup reason. + </tp:docstring> + </arg> + + <arg direction="in" name="Detailed_Removal_Reason" type="s" + tp:type="DBus_Error_Name"> + <tp:docstring> + A more specific reason for the content removal, if one is + available, or an empty string. + </tp:docstring> + </arg> + + <arg direction="in" name="Message" type="s"> + <tp:docstring> + A human-readable message for the reason of removing the + content, such as "Fatal streaming failure" or "no codec + intersection". This property can be left empty if no reason + is to be given. + </tp:docstring> + </arg> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NetworkError" /> + <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:immutable="yes"> + <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:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The name of the content.</p> + + <tp:rationale> + The content name property should be meaningful, so should be + given a name which is significant to the user. The name + could be the "audio" or "video" string localized, or perhaps + include some string identifying the source, such as a webcam + identifier. + </tp:rationale> + </tp:docstring> + </property> + + <property name="Type" tp:name-for-bindings="Type" + type="u" tp:type="Media_Stream_Type" access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The media type of this content.</p> + </tp:docstring> + </property> + + <tp:enum name="Call_Content_Disposition" type="u"> + <tp:docstring> + The disposition of this content, which defines whether to + automatically start sending data on the streams when + <tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref> is + called on the channel. + </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="Initial" value="1"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The content was initially part of the call. When + <tp:dbus-ref + namespace="ofdT.Channel.Type.Call.DRAFT">Accept</tp:dbus-ref> + is called on the channel, all streams of this content with + <tp:dbus-ref + namespace="ofdT.Call.Stream.DRAFT">LocalSendingState</tp:dbus-ref> + set to <tp:type>Sending_State</tp:type>_Pending_Send will be + moved to <tp:type>Sending_State</tp:type>_Sending as if + <tp:dbus-ref + namespace="ofdT.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:immutable="yes"> + <tp:docstring> + The disposition of this content. + </tp:docstring> + </property> + + <signal name="StreamsAdded" tp:name-for-bindings="Streams_Added"> + <tp:changed version="0.21.2">plural version, renamed from + StreamAdded</tp:changed> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Emitted when streams are added to a call.</p> + </tp:docstring> + <arg name="Streams" type="ao"> + <tp:docstring> + The <tp:dbus-ref + namespace="ofdT.Call">Stream.DRAFT</tp:dbus-ref>s which were + added. + </tp:docstring> + </arg> + </signal> + + <signal name="StreamsRemoved" tp:name-for-bindings="Streams_Removed"> + <tp:changed version="0.21.2">plural version, renamed from + StreamRemoved</tp:changed> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Emitted when streams are removed from a call</p> + </tp:docstring> + <arg name="Streams" type="ao"> + <tp:docstring> + The <tp:dbus-ref + namespace="ofdT.Call">Stream.DRAFT</tp:dbus-ref>s which were + 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="ofdT.Call" + >Stream.DRAFT</tp:dbus-ref> objects that exist in this + content.</p> + + <tp:rationale> + 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. + </tp:rationale> + + <p>Change notification is through the + <tp:member-ref>StreamsAdded</tp:member-ref> and + <tp:member-ref>StreamsRemoved</tp:member-ref> signals.</p> + </tp:docstring> + </property> + + <tp:enum name="Call_Content_Packetization_Type" type="u"> + <tp:added version="0.21.2"/> + <tp:docstring> + A packetization method that can be used for a content. + </tp:docstring> + + <tp:enumvalue suffix="RTP" value="0"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + Real-time Transport Protocol, as documented by RFC 3550. + </tp:docstring> + </tp:enumvalue> + + <tp:enumvalue suffix="Raw" value="1"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + Raw media. + </tp:docstring> + </tp:enumvalue> + + <tp:enumvalue suffix="MSN_Webcam" value="2"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + MSN webcam. This is the video-only one-way type which was + used in earlier versions of WLM. Although no longer used, + modern WLM clients still support the MSN webcam protocol. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + <property name="Packetization" tp:name-for-bindings="Packetization" + type="u" tp:type="Call_Content_Packetization_Type" access="read" + tp:immutable="yes"> + <tp:added version="0.21.2"/> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The packetization method in use for this content.</p> + </tp:docstring> + </property> + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/telepathy-yell/Call_Content_Codec_Offer.xml b/telepathy-yell/Call_Content_Codec_Offer.xml new file mode 100644 index 0000000..e0a791f --- /dev/null +++ b/telepathy-yell/Call_Content_Codec_Offer.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" ?> +<node name="/Call_Content_Codec_Offer" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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. + </tp:docstring> + + <method name="Accept" tp:name-for-bindings="Accept"> + <arg name="Codecs" direction="in" + type="a(usuua{ss})" tp:type="Codec[]"> + <tp:docstring> + The local codec mapping to send to the remote contacts and + to use in the <tp:dbus-ref + namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref>. + </tp:docstring> + </arg> + <tp:docstring> + Accept the updated Codec mapping and update the local mapping. + </tp:docstring> + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> + <tp:docstring> + The codecs given as the argument are invalid in some way. + </tp:docstring> + </tp:error> + </tp:possible-errors> + </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="Interfaces" tp:name-for-bindings="Interfaces" + type="as" tp:type="DBus_Interface[]" access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Extra interfaces provided by this codec offer. This SHOULD + NOT include the CodecOffer interface itself, and cannot change + once the content has been created.</p> + </tp:docstring> + </property> + + <property name="RemoteContactCodecMap" + tp:name-for-bindings="Remote_Contact_Codec_Map" + type="a{ua(usuua{ss})}" tp:type="Contact_Codec_Map" access="read" + tp:immutable="yes"> + <tp:docstring> + A map from remote contact to the list of codecs he or she + supports. + </tp:docstring> + </property> + + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/telepathy-yell/Call_Content_Interface_Media.xml b/telepathy-yell/Call_Content_Interface_Media.xml new file mode 100644 index 0000000..0552111 --- /dev/null +++ b/telepathy-yell/Call_Content_Interface_Media.xml @@ -0,0 +1,245 @@ +<?xml version="1.0" ?> +<node name="/Call_Content_Interface_Media" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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.DRAFT"/> + + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Interface to use by a software implementation of media + streaming.</p> + + <p>On new <tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref> channels, + handlers should wait for <tp:dbus-ref + namespace="ofdT.Call.Content">CodecOffer.DRAFT</tp:dbus-ref> + objects to appear (one will either already be present, or will + appear at some point in the channel's lifetime).</p> + + <p>If the Call is incoming, then the codec offer's <tp:dbus-ref + namespace="ofdT.Call.Content.CodecOffer.DRAFT">RemoteContactCodecMap</tp:dbus-ref> + will already be filled with the codec information of the + remote contacts. Depending on the protocol, an outgoing call's + <tp:dbus-ref + namespace="ofdT.Call.Content.CodecOffer.DRAFT">RemoteContactCodecMap</tp:dbus-ref> + will either be filled with remote contact codec information, or + it will be empty. If empty, then this SHOULD be interpreted to + mean that all codecs are supported. Once a compatible list of + codecs has been decided, <tp:dbus-ref + namespace="ofdT.Call.Content">CodecOffer.DRAFT.Accept</tp:dbus-ref> + should be called with the details of these codecs.</p> + + </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 contact to the list of codecs he or she supports. + </tp:docstring> + <tp:member name="Handle" type="u" tp:type="Contact_Handle"> + <tp:docstring> + A contact handle. + </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="ofdT.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="ofdT.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 contact to his or her 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="UpdateCodecs" tp:name-for-bindings="Update_Codecs"> + <tp:docstring> + Update the local codec mapping. This method should only be + used during an existing call to update the 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> + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable"> + <tp:docstring> + Raised when not used during an existing call to update the codec mapping. + </tp:docstring> + </tp:error> + </tp:possible-errors> + </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 the + <tp:member-ref>CodecsChanged</tp:member-ref> signal.</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="ofdT.Call.Content" + >CodecOffer.DRAFT</tp:dbus-ref> appears. The streaming + implementation MUST respond by calling the <tp:dbus-ref + namespace="ofdT.Call.Content.CodecOffer.DRAFT" + >Accept</tp:dbus-ref> or <tp:dbus-ref + namespace="ofdT.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="ofdT.Call.Content" + >CodecOffer.DRAFT.RemoteContactCodecMap</tp:dbus-ref> property + of the codec offer.</p> + + <tp:rationale> + Having the <tp:dbus-ref + namespace="ofdT.Call.Content.CodecOffer.DRAFT">RemoteContactCodecMap</tp:dbus-ref> + property here saves a D-Bus round-trip - it shouldn't be + necessary to get the property from the CodecOffer object, in + practice. + </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="ofdT.Call.Content" + >CodecOffer.DRAFT</tp:dbus-ref> object, and its + <tp:dbus-ref namespace="ofdT.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> + Having the <tp:dbus-ref + namespace="ofdT.Call.Content.CodecOffer.DRAFT">RemoteContactCodecMap</tp:dbus-ref> + property here saves a D-Bus round-trip - it shouldn't be + necessary to get the property from the CodecOffer object, in + practice. + </tp:rationale> + + <p>Change notification is via the + <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) signals.</p> + </tp:docstring> + </property> + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/telepathy-yell/Call_Stream.xml b/telepathy-yell/Call_Stream.xml new file mode 100644 index 0000000..b693989 --- /dev/null +++ b/telepathy-yell/Call_Stream.xml @@ -0,0 +1,261 @@ +<?xml version="1.0" ?> +<node name="/Call_Stream" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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 <tp:dbus-ref + namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref>. + </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 + <tp:member-ref>LocalSendingState</tp:member-ref> should + change to <tp:type>Sending_State</tp:type>_Sending, if it isn't + already.</p> + + <p>If False, the + <tp:member-ref>LocalSendingState</tp:member-ref> should + change to <tp:type>Sending_State</tp:type>_None, if it isn't + already.</p> + </tp:docstring> + </arg> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented" /> + </tp:possible-errors> + </method> + + <method name="RequestReceiving" tp:name-for-bindings="Request_Receiving"> + <tp:docstring> + <p>Request that a remote contact stops or starts sending on + this stream.</p> + + <p>The <tp:member-ref>CanRequestReceiving</tp:member-ref> + property defines whether the protocol allows the local user to + request the other side start sending on this stream.</p> + </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.InvalidHandle"/> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"> + <tp:docstring> + The request contact is valid but is not involved in this + stream. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"> + <tp:docstring> + The protocol does not allow the local user to request the + other side starts sending on this stream. + </tp:docstring> + </tp:error> + </tp:possible-errors> + </method> + + <signal name="RemoteMembersChanged" + tp:name-for-bindings="Remote_Members_Changed"> + <tp:changed version="0.21.2">renamed from SendersChanged to MembersChanged</tp:changed> + <tp:changed version="0.21.UNRELEASED">renamed from MembersChanged to RemoteMembersChanged</tp:changed> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + Emitted when <tp:member-ref>RemoteMembers</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 members who were added, + and the members 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 <tp:member-ref>RemoteMembers</tp:member-ref> + property, as a result of the contact leaving this stream + </tp:docstring> + </arg> + </signal> + + <signal name="LocalSendingStateChanged" + tp:name-for-bindings="Local_Sending_State_Changed"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + Emitted when <tp:member-ref>LocalSendingState</tp:member-ref> changes. + </tp:docstring> + + <arg name="State" type="u" tp:type="Sending_State"> + <tp:docstring> + The new value of + <tp:member-ref>LocalSendingState</tp:member-ref>. + </tp:docstring> + </arg> + </signal> + + <tp:enum name="Sending_State" type="u"> + <tp:docstring> + Enum 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:enumvalue suffix="Pending_Stop_Sending" value="3"> + <tp:docstring> + The contact has been asked to stop sending media. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + <tp:mapping name="Contact_Sending_State_Map"> + <tp:docstring> + A map from a contact to his or her sending state. + </tp:docstring> + <tp:member name="Contact" type="u" tp:type="Contact_Handle"> + <tp:docstring> + The contact handle. + </tp:docstring> + </tp:member> + <tp:member name="Sending" type="u" tp:type="Sending_State"> + <tp:docstring> + The sending state of the contact. + </tp:docstring> + </tp:member> + </tp:mapping> + + <property name="Interfaces" tp:name-for-bindings="Interfaces" + type="as" tp:type="DBus_Interface[]" access="read" tp:immutable="yes"> + <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="RemoteMembers" tp:name-for-bindings="Remote_Members" + type="a{uu}" access="read" tp:type="Contact_Sending_State_Map"> + <tp:changed version="0.21.2">renamed from Senders</tp:changed> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A map from remote contacts to their sending state. The + local user's sending state is shown in + <tp:member-ref>LocalSendingState</tp:member-ref>.</p> + + <p><tp:type>Sending_State</tp:type>_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> + + <property name="LocalSendingState" tp:name-for-bindings="Local_Sending_State" + type="u" access="read" tp:type="Sending_State"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The local user's sending state. Media sent on this stream + should be assumed to be received, directly or indirectly, by + every other contact in the + <tp:member-ref>RemoteMembers</tp:member-ref> mapping. Change + notification is given via the + <tp:member-ref>LocalSendingStateChanged</tp:member-ref> + signal.</p> + + <tp:rationale> + Implementations of the first Call draft had the self handle + in the <tp:member-ref>RemoteMembers</tp:member-ref> (then + called Members) map and this showed that it's annoying + having to keep track of the self handle so that it can be + special-cased. + </tp:rationale> + + <p>A value of <tp:type>Sending_State</tp:type>_Pending_Send for + this property indicates that the other side requested the + local user start sending media, which can be done by calling + <tp:member-ref>SetSending</tp:member-ref>. When a call is + accepted, all initial contents with streams that have a + local sending state of + <tp:type>Sending_State</tp:type>_Pending_Send are + automatically set to sending. For example, on an incoming + call it means you need to <tp:dbus-ref + namespace="ofdT.Channel.Type.Call.DRAFT">Accept</tp:dbus-ref> + to start the actual call, on an outgoing call it might mean + you need to call <tp:dbus-ref + namespace="ofdT.Channel.Type.Call.DRAFT">Accept</tp:dbus-ref> + before actually starting the call.</p> + </tp:docstring> + </property> + + <property name="CanRequestReceiving" tp:name-for-bindings="Can_Request_Receiving" + type="b" access="read" tp:immutable="yes"> + <tp:added version="0.21.2"/> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>If true, the user can request that a remote contact starts + sending on this stream.</p> + + <tp:rationale>Not all protocols allow the user to ask the + other side to start sending media.</tp:rationale> + </tp:docstring> + </property> + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/telepathy-yell/Call_Stream_Endpoint.xml b/telepathy-yell/Call_Stream_Endpoint.xml new file mode 100644 index 0000000..4818168 --- /dev/null +++ b/telepathy-yell/Call_Stream_Endpoint.xml @@ -0,0 +1,182 @@ +<?xml version="1.0" ?> +<node name="/Call_Stream_Endpoint" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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"> + <p>This object represents an endpoint for a stream. In a one-to-one + call, there will be one (bidirectional) stream per content and + one endpoint per stream (as there is only one remote + contact). In a multi-user call there is a stream for each remote + contact and each stream has one endpoint as it refers to the one + physical machine on the other end of the stream.</p> + + <p>The multiple endpoint use case appears when SIP call forking + is used. Unlike jingle call forking (which is just making + multiple jingle calls to different resources appear as one + call), SIP call forking is actually done at the server so you + have one stream to the remote contact and then and endpoint for + each SIP client to be called.</p> + </tp:docstring> + + <property name="RemoteCredentials" + tp:name-for-bindings="Remote_Credentials" + type="(ss)" tp:type="Stream_Credentials" access="read"> + <tp:docstring> + The ICE credentials used for all candidates. If each candidate + has different credentials, then this property SHOULD be ("", + ""). Per-candidate credentials are set in the + <tp:type>Candidate</tp:type>'s + <tp:type>Candidate_Info</tp:type> a{sv}. + </tp:docstring> + </property> + + <signal name="RemoteCredentialsSet" + tp:name-for-bindings="Remote_Credentials_Set"> + <arg name="Username" type="s"> + <tp:docstring> + The username set. + </tp:docstring> + </arg> + <arg name="Password" type="s"> + <tp:docstring> + The password set. + </tp:docstring> + </arg> + <tp:docstring> + Emitted when the remote ICE credentials for the endpoint are + set. If each candidate has different credentials, then this + signal will never be fired. + </tp:docstring> + </signal> + + <property name="RemoteCandidates" tp:name-for-bindings="Remote_Candidates" + type="a(usqa{sv})" tp:type="Candidate[]" access="read"> + <tp:docstring> + A list of candidates for this endpoint. + </tp:docstring> + </property> + + <signal name="RemoteCandidatesAdded" + tp:name-for-bindings="Remote_Candidates_Added"> + <tp:docstring> + Emitted when remote candidates are added to the + <tp:member-ref>RemoteCandidates</tp:member-ref> property. + </tp:docstring> + <arg name="Candidates" + type="a(usqa{sv})" tp:type="Candidate[]"> + <tp:docstring> + The candidates that were added. + </tp:docstring> + </arg> + </signal> + + <signal name="CandidateSelected" + tp:name-for-bindings="Candidate_Selected"> + <tp:docstring> + Emitted when a candidate is selected for use in the stream. + </tp:docstring> + <arg name="Candidate" + type="(usqa{sv})" tp:type="Candidate"> + <tp:docstring> + The candidate that has been selected. + </tp:docstring> + </arg> + </signal> + + <property name="SelectedCandidate" + tp:name-for-bindings="Selected_Candidate" + type="(usqa{sv})" tp:type="Candidate" access="read"> + <tp:docstring> + The candidate that has been selected for use to stream packets + to the remote contact. Change notification is given via the + the <tp:member-ref>CandidateSelected</tp:member-ref> signal. + </tp:docstring> + </property> + + <method name="SetSelectedCandidate" + tp:name-for-bindings="Set_Selected_Candidate"> + <tp:docstring> + Set the value of + <tp:member-ref>CandidateSelected</tp:member-ref>. + </tp:docstring> + <arg name="Candidate" + type="(usqa{sv})" tp:type="Candidate" direction="in"> + <tp:docstring> + The candidate that has been selected. + </tp:docstring> + </arg> + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"/> + </tp:possible-errors> + </method> + + <property name="StreamState" tp:name-for-bindings="Stream_State" + type="u" tp:type="Media_Stream_State" + access="read"> + <tp:docstring> + The stream state of the endpoint. + </tp:docstring> + </property> + + <signal name="StreamStateChanged" + tp:name-for-bindings="Stream_State_Changed"> + <tp:docstring> + Emitted when the <tp:member-ref>StreamState</tp:member-ref> + property changes. + </tp:docstring> + <arg name="state" type="u" tp:type="Media_Stream_State"> + <tp:docstring> + The new <tp:member-ref>StreamState</tp:member-ref> value. + </tp:docstring> + </arg> + </signal> + + <method name="SetStreamState" + tp:name-for-bindings="Set_Stream_State"> + <tp:docstring> + Change the <tp:member-ref>StreamState</tp:member-ref> of the + endpoint. + </tp:docstring> + <arg direction="in" name="State" type="u" tp:type="Media_Stream_State"> + <tp:docstring> + The requested stream state. + </tp:docstring> + </arg> + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.InvalidArgument"/> + <tp:error name="org.freedesktop.Telepathy.Error.NotAvailable"/> + </tp:possible-errors> + </method> + + <property name="Transport" tp:name-for-bindings="Transport" + type="u" tp:type="Stream_Transport_Type" access="read"> + <tp:docstring> + The transport type for the stream endpoint. + </tp:docstring> + </property> + + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/telepathy-yell/Call_Stream_Interface_Media.xml b/telepathy-yell/Call_Stream_Interface_Media.xml new file mode 100644 index 0000000..3d4fb13 --- /dev/null +++ b/telepathy-yell/Call_Stream_Interface_Media.xml @@ -0,0 +1,406 @@ +<?xml version="1.0" ?> +<node name="/Call_Stream_Interface_Media" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Ltd.</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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.DRAFT"/> + + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + [FIXME] + </tp:docstring> + + <method name="SetCredentials" tp:name-for-bindings="Set_Credentials"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Used to set the username fragment and password for streams that have + global credentials.</p> + </tp:docstring> + <arg name="Username" type="s" direction="in"> + <tp:docstring> + The username to use when authenticating on the stream. + </tp:docstring> + </arg> + <arg name="Password" type="s" direction="in"> + <tp:docstring> + The password to use when authenticating on the stream. + </tp:docstring> + </arg> + </method> + + <tp:mapping name="Candidate_Info"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Extra information about the candidate. Allowed and mandatory keys + depend on the transport protocol used. The following keys are commenly + used:</p> + + <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 the + <tp:member-ref>LocalCandidates</tp:member-ref> property and + signal them to the remote contact(s). + </tp:docstring> + <arg name="Candidates" direction="in" + type="a(usqa{sv})" tp:type="Candidate[]"> + <tp:docstring> + The candidates to be added. + </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:docstring> + </method> + + <tp:enum type="u" name="Stream_Transport_Type"> + <tp:changed version="0.21.2">WLM_8_5 was removed</tp:changed> + <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="ofdT.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 RFC + 5245. Note that this value covers ICE-UDP only. + [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_2009" value="3"> + <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:enumvalue suffix="SHM" value="4"> + <tp:added version="0.21.2"/> + <tp:docstring> + Shared memory transport, as implemented by the GStreamer + shmsrc and shmsink plugins. + </tp:docstring> + </tp:enumvalue> + </tp:enum> + + <property name="Transport" tp:name-for-bindings="Transport" + type="u" tp:type="Stream_Transport_Type" access="read" tp:immutable="yes"> + <tp:docstring> + The transport for this stream. + </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 the + <tp:member-ref>LocalCandidatesAdded</tp:member-ref> signal. + </tp:docstring> + </property> + + <signal name="LocalCandidatesAdded" + tp:name-for-bindings="Local_Candidates_Added"> + <tp:docstring> + Emitted when local candidates are added to the + <tp:member-ref>LocalCandidates</tp:member-ref> property. + </tp:docstring> + <arg name="Candidates" type="a(usqa{sv})" tp:type="Candidate[]"> + <tp:docstring> + Candidates that have been added. + </tp:docstring> + </arg> + </signal> + + <tp:struct name="Stream_Credentials"> + <tp:docstring>A username and 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 the + <tp:member-ref>LocalCredentialsChanged</tp:member-ref> signal. + </tp:docstring> + </property> + + <signal name="LocalCredentialsChanged" + tp:name-for-bindings="Local_Credentials_Changed"> + <tp:changed version="0.21.2">renamed from LocalCredentailsSet</tp:changed> + <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> + + <signal name="RelayInfoChanged" + tp:name-for-bindings="Relay_Info_Changed"> + <tp:docstring> + Emitted when the value of + <tp:member-ref>RelayInfo</tp:member-ref> changes. + </tp:docstring> + <arg name="Relay_Info" type="aa{sv}" tp:type="String_Variant_Map[]" /> + </signal> + + <signal name="STUNServersChanged" + tp:name-for-bindings="STUN_Servers_Changed"> + <tp:docstring> + Emitted when the value of + <tp:member-ref>STUNServers</tp:member-ref> changes. + </tp:docstring> + <arg name="Servers" type="a(sq)" tp:type="Socket_Address_IP[]" /> + </signal> + + <property name="STUNServers" tp:name-for-bindings="STUN_Servers" + type="a(sq)" tp:type="Socket_Address_IP[]" access="read"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The IP addresses of possible STUN servers to use for NAT + traversal, as dotted-quad IPv4 address literals or RFC2373 + IPv6 address literals. Change notification is via the + <tp:member-ref>STUNServersChanged</tp:member-ref> + signal. The IP addresses MUST NOT be given as DNS hostnames.</p> + + <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> + + <p>Change notification is given via the + <tp:member-ref>RelayInfoChanged</tp:member-ref> signal.</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>HasServerInfo</tp:member-ref> property is + now true.</p> + </tp:docstring> + </signal> + + <property name="HasServerInfo" type="b" + tp:name-for-bindings="Has_Server_Info" access="read"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>True if all 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> + Streaming implementations that can't cope with STUN and + relay servers being added later SHOULD wait for this + property to become true before proceeding. + </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="Endpoints_Added" type="ao"> + <tp:docstring> + Endpoints that were added. + </tp:docstring> + </arg> + <arg name="Endpoints_Removed" 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 <tp:dbus-ref namespace="ofdT.Call.Stream" + >Endpoint.DRAFT</tp:dbus-ref> objects 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/telepathy-yell/Channel_Type_Call.xml b/telepathy-yell/Channel_Type_Call.xml new file mode 100644 index 0000000..eb1a663 --- /dev/null +++ b/telepathy-yell/Channel_Type_Call.xml @@ -0,0 +1,1425 @@ +<?xml version="1.0" ?> +<node name="/Channel_Type_Call" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2009-2010 Collabora Limited</tp:copyright> + <tp:copyright>Copyright © 2009-2010 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. Call + channels supersede the old <tp:dbus-ref + namespace="ofdT.Channel.Type">StreamedMedia</tp:dbus-ref> + channel type. Call channels are much more flexible than its + predecessor and allow more than two participants.</p> + + <p>Handlers are advised against executing all the media + signalling, codec and candidate negotiation themselves but + instead use a helper library such as <a + href="http://telepathy.freedesktop.org/doc/telepathy-farsight/">telepathy-farsight</a> + which when given a new Call channel will set up the + transports and codecs and create GStreamer pads which + can be added to the handler UI. This is useful as it means + the handler does not have to worry how exactly the + connection between the call participants is being made.</p> + + <p>The <tp:dbus-ref + namespace="ofdT.Channel">TargetHandle</tp:dbus-ref> and + <tp:dbus-ref namespace="ofdT.Channel">TargetID</tp:dbus-ref> + properties in a Call channel refer to the contact that the + user initially called, or which contact initially called the + user. Even in a conference call, where there are multiple + contacts in the call, these properties refer to the + initial contact, who might have left the conference since + then. As a result, handlers should not rely on these + properties.</p> + + <h4>Contents</h4> + + <p><tp:dbus-ref + namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref> objects + represent the actual media that forms the Call (for example an + audio content and a video content). Calls always have one or + more Content objects associated with them.</p> + + <p><tp:dbus-ref + namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref> objects have + one or more stream associated with them. More information on + these streams and how to maniuplate them can be found on the + <tp:dbus-ref namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref> + interface page.</p> + + <h4>Outgoing calls</h4> + + <p>To make an audio-only call to a contact <tt>foo@example.com</tt> + handlers should call:</p> + + <blockquote> + <pre> +<tp:dbus-ref namespace="ofdT.Connection.Interface.Requests">CreateChannel</tp:dbus-ref>({ + ...<tp:dbus-ref namespace="ofdT.Channel">ChannelType</tp:dbus-ref>: ...<tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref>, + ...<tp:dbus-ref namespace="ofdT.Channel">TargetHandleType</tp:dbus-ref>: Contact, + ...<tp:dbus-ref namespace="ofdT.Channel">TargetID</tp:dbus-ref>: 'foo@example.com', + ...<tp:member-ref>InitialAudio</tp:member-ref>: True, +})</pre></blockquote> + + <p>As always, <tp:dbus-ref + namespace="ofdT.Channel">TargetHandle</tp:dbus-ref> may be used + in place of + <tp:dbus-ref namespace="ofdT.Channel">TargetID</tp:dbus-ref> + if the contact's handle is already known. To make an audio + and video call, the handler should also specify + <tp:member-ref>InitialVideo</tp:member-ref> The + connection manager SHOULD return a channel whose immutable + properties contain the local user as the <tp:dbus-ref + namespace="ofdT.Channel">InitiatorHandle</tp:dbus-ref>, the + remote contact as the <tp:dbus-ref + namespace="ofdT.Channel">TargetHandle</tp:dbus-ref>, + <tp:dbus-ref namespace="ofdT.Channel">Requested</tp:dbus-ref> = + <code>True</code> (indicating the call is outgoing).</p> + + <p>After a new Call channel is requested, the + <tp:member-ref>CallState</tp:member-ref> property will be + <tp:type>Call_State</tp:type>_Pending_Initiator. As the local + user is the initiator, the call must be accepted by the handler + by calling the <tp:member-ref>Accept</tp:member-ref> method. + At this point, <tp:member-ref>CallState</tp:member-ref> changes + to <tp:type>Call_State</tp:type>_Pending_Receiver which signifies + that the call is ringing, waiting for the remote contact to + accept the call. All changes to + <tp:member-ref>CallState</tp:member-ref> property are signalled + using the <tp:member-ref>CallStateChanged</tp:member-ref> + signal.</p> + + <p>When the call is accepted by the remote contact, the + <tp:member-ref>CallStateChanged</tp:member-ref> signal fires + again to show that <tp:member-ref>CallState</tp:member-ref> = + <tp:type>Call_State</tp:type>_Accepted.</p> + + <p>At this point <a + href="http://telepathy.freedesktop.org/doc/telepathy-farsight/">telepathy-farsight</a> + will signal that a pad is available for the handler to show + in the user interface.</p> + + <h5>Missed calls</h5> + + <p>If the remote contact does not accept the call in time, then + the call can be terminated by the server. Note that this only + happens in some protocols. Most XMPP clients, for example, do + not do this and rely on the call initiator terminating the call. + A missed call is shown in a Call channel by the + <tp:member-ref>CallState</tp:member-ref> property changing to + <tp:type>Call_State</tp:type>_Ended, and the + <tp:member-ref>CallStateReason</tp:member-ref> property changing + to (remote contact, + <tp:type>Call_State_Change_Reason</tp:type>_No_Answer, "").</p> + + <h5>Rejected calls</h5> + + <p>If the remote contact decides he or she does not feel like + talking to the local user, he or she can reject his or her + incoming call. This will be shown in the Call channel by + <tp:member-ref>CallState</tp:member-ref> changing to + <tp:type>Call_State</tp:type>_Ended and the + <tp:member-ref>CallStateReason</tp:member-ref> property + changing to (remote contact, + <tp:type>Call_State</tp:type>_Change_Reason_User_Requested, + "org.freedesktop.Telepathy.Error.Rejected").</p> + + <h4>Incoming calls</h4> + + <p>When an incoming call occurs, something like the following + <tp:dbus-ref + namespace="ofdT.Connection.Interface.Requests">NewChannels</tp:dbus-ref> + signal will occur:</p> + + <blockquote> + <pre> +<tp:dbus-ref namespace="ofdT.Connection.Interface.Requests">NewChannels</tp:dbus-ref>([ + /org/freedesktop/Telepathy/Connection/foo/bar/foo_40bar_2ecom/CallChannel, + { + ...<tp:dbus-ref namespace="ofdT.Channel">ChannelType</tp:dbus-ref>: ...<tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref>, + ...<tp:dbus-ref namespace="ofdT.Channel">TargetHandleType</tp:dbus-ref>: Contact, + ...<tp:dbus-ref namespace="ofdT.Channel">TargetID</tp:dbus-ref>: 'foo@example.com', + ...<tp:dbus-ref namespace="ofdT.Channel">TargetHandle</tp:dbus-ref>: 42, + ...<tp:dbus-ref namespace="ofdT.Channel">Requested</tp:dbus-ref>: False, + ...<tp:member-ref>InitialAudio</tp:member-ref>: True, + ...<tp:member-ref>InitialVideo</tp:member-ref>: True, + ...<tp:member-ref>InitialAudioName</tp:member-ref>: "audio", + ...<tp:member-ref>InitialVideoName</tp:member-ref>: "video", + ...<tp:member-ref>MutableContents</tp:member-ref>: True, + }])</pre></blockquote> + + <p>The <tp:member-ref>InitialAudio</tp:member-ref> and + <tp:member-ref>InitialVideo</tp:member-ref> properties show that + the call has been started with two contents: one for audio + streaming and one for video streaming. The + <tp:member-ref>InitialAudioName</tp:member-ref> and + <tp:member-ref>InitialVideoName</tp:member-ref> properties also + show that the aforementioned audio and video contents have names + "audio" and "video".</p> + + <p>Once the handler has notified the local user that there is an + incoming call waiting for acceptance, the handler should call + <tp:member-ref>SetRinging</tp:member-ref> to let the CM know. + The new channel should also be given to telepathy-farsight to + work out how the two participants will connect together. + telepathy-farsight will call the appropriate methods on the call's + <tp:dbus-ref namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref>s + to negotiate codecs and transports.</p> + + <p>To pick up the call, the handler should call + <tp:member-ref>Accept</tp:member-ref>. The + <tp:member-ref>CallState</tp:member-ref> property changes to + <tp:type>Call_State</tp:type>_Accepted and once media is + being transferred, telepathy-farsight will notify the + handler of a new pad to be shown to the local user in the + UI</p> + + <p>To reject the call, the handler should call the + <tp:member-ref>Hangup</tp:member-ref> method. The + <tp:member-ref>CallState</tp:member-ref> property will change to + <tp:type>Call_State</tp:type>_Ended and the + <tp:member-ref>CallStateReason</tp:member-ref> property will + change to (self handle, + <tp:type>Call_State_Change_Reason</tp:type>_User_Requested, + "org.freedesktop.Telepathy.Error.Rejected").</p> + + <h4>Ongoing calls</h4> + + <h5>Adding and removing contents</h5> + + <p>When a call is open, new contents can be added as long as the + CM supports it. The + <tp:member-ref>MutableContents</tp:member-ref> property will let + the handler know whether further contents can be added or + existing contents removed. An example of this is starting a + voice call between a contact and then adding a video content. + To do this, the should call + <tp:member-ref>AddContent</tp:member-ref> like this:</p> + + <blockquote> + <pre><tp:member-ref>AddContent</tp:member-ref>("video", + <tp:type>Media_Stream_Type</tp:type>_Video)</pre> + </blockquote> + + <p>Assuming no errors, the new video content will be added to + the call. telepathy-farsight will pick up the new content and + perform the transport and codec negotiation automatically. + telpathy-farsight will signal when the video is ready to + show in the handler's user interface.</p> + + <p>A similar method is used for removing contents from a call, + except that the <tp:dbus-ref + namespace="ofdT.Call.Content.DRAFT">Remove</tp:dbus-ref> method + is on the <tp:dbus-ref + namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref> object.</p> + + <h5>Ending the call</h5> + + <p>To end the call, the handler should call the + <tp:member-ref>Hangup</tp:member-ref> method. The + <tp:member-ref>CallState</tp:member-ref> property will change to + <tp:type>Call_State</tp:type>_Ended and + <tp:member-ref>CallStateReason</tp:member-ref> will change + to (self handle, + <tp:type>Call_State_Change_Reason</tp:type>_User_Requested, + "org.freedesktop.Telepathy.Error.Cancelled").</p> + + <p>If the other participant hangs up first then the + <tp:member-ref>CallState</tp:member-ref> property will change to + <tp:type>Call_State</tp:type>_Ended and + <tp:member-ref>CallStateReason</tp:member-ref> will change + to (remote contact, + <tp:type>Call_State_Change_Reason</tp:type>_User_Requested, + "org.freedesktop.Telepathy.Error.Terminated").</p> + + <h4>Multi-party calls</h4> + + [TODO] + + <h4>Call states</h4> + + <p>There are many combinations of the + <tp:member-ref>CallState</tp:member-ref> and + <tp:member-ref>CallStateReason</tp:member-ref> properties which + mean different things. Here is a table to try to make these + meanings clearer:</p> + + <table> + <tr> + <th rowspan="2"><tp:dbus-ref namespace="ofdT.Channel">Requested</tp:dbus-ref></th> + <th rowspan="2"><tp:member-ref>CallState</tp:member-ref></th> + <th colspan="3"><tp:member-ref>CallStateReason</tp:member-ref></th> + <th rowspan="2">Meaning</th> + </tr> + <tr> + <th>Actor</th> + <th>Reason</th> + <th>DBus_Reason</th> + </tr> + <!-- Pending_Initiator --> + <tr> + <td>True</td> + <td><tp:type>Call_State</tp:type>_Pending_Initiator</td> + <td>Self handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td>""</td> + <td>The outgoing call channel is waiting for the local user to call <tp:member-ref>Accept</tp:member-ref>.</td> + </tr> + <!-- Pending_Receiver --> + <tr> + <td>True</td> + <td><tp:type>Call_State</tp:type>_Pending_Receiver</td> + <td>Self handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td>""</td> + <td>The outgoing call is waiting for the remote contact to pick up the call.</td> + </tr> + <tr> + <td>False</td> + <td><tp:type>Call_State</tp:type>_Pending_Receiver</td> + <td>0</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_Unknown</td> + <td>""</td> + <td>The incoming call is waiting for the local user to call <tp:member-ref>Accept</tp:member-ref> on the call.</td> + </tr> + <!-- Accepted --> + <tr> + <td>True</td> + <td><tp:type>Call_State</tp:type>_Accepted</td> + <td>Remote contact handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td>""</td> + <td>The remote contact accepted the outgoing call.</td> + </tr> + <tr> + <td>False</td> + <td><tp:type>Call_State</tp:type>_Accepted</td> + <td>Self handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td>""</td> + <td>The local user accepted the incoming call.</td> + </tr> + <!-- Ended --> + <tr> + <td>True or False</td> + <td><tp:type>Call_State</tp:type>_Ended</td> + <td>Self handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td><tp:error-ref>Cancelled</tp:error-ref></td> + <td>The local user hung up the incoming or outgoing call.</td> + </tr> + <tr> + <td>True or False</td> + <td><tp:type>Call_State</tp:type>_Ended</td> + <td>Remote contact handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td><tp:error-ref>Terminated</tp:error-ref></td> + <td>The remote contact hung up the incoming or outgoing call.</td> + </tr> + <tr> + <td>True</td> + <td><tp:type>Call_State</tp:type>_Ended</td> + <td>Remote contact handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_No_Answer</td> + <td>""</td> + <td>The outgoing call was not picked up and the call ended.</td> + </tr> + <tr> + <td>False</td> + <td><tp:type>Call_State</tp:type>_Ended</td> + <td>Remote contact handle</td> + <td><tp:type>Call_State_Change_Reason</tp:type>_User_Requested</td> + <td><tp:error-ref>PickedUpElsewhere</tp:error-ref></td> + <td>The incoming call was ended because it was picked up elsewhere.</td> + </tr> + </table> + + <h4>Requestable channel classes</h4> + + <p>The <tp:dbus-ref + namespace="ofdT.Connection.Interface.Requests">RequestableChannelClasses</tp:dbus-ref> + for <tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref> channels + can be:</p> + + <blockquote> + <pre> +[( Fixed = { ...<tp:dbus-ref namespace="ofdT.Channel">ChannelType</tp:dbus-ref>: ...<tp:dbus-ref namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref>, + ...<tp:dbus-ref namespace="ofdT.Channel">TargetHandleType</tp:dbus-ref>: Contact, + ...<tp:member-ref>InitialVideo</tp:member-ref>: True + }, + Allowed = [ ...<tp:member-ref>InitialVideoName</tp:member-ref>, + ...<tp:member-ref>InitialAudio</tp:member-ref>, + ...<tp:member-ref>InitialAudioName</tp:member-ref> + ] +), +( Fixed = { ...<tp:dbus-ref namespace="ofdT.Channel">ChannelType</tp:dbus-ref>: ...<tp:dbus-ref namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref>, + ...<tp:dbus-ref namespace="ofdT.Channel">TargetHandleType</tp:dbus-ref>: Contact, + ...<tp:member-ref>InitialAudio</tp:member-ref>: True + }, + Allowed = [ ...<tp:member-ref>InitialAudioName</tp:member-ref>, + ...<tp:member-ref>InitialVideo</tp:member-ref>, + ...<tp:member-ref>InitialVideoName</tp:member-ref> + ] +)]</pre></blockquote> + + <p>Clients aren't allowed to make outgoing calls that have + neither initial audio nor initial video. Clearly, CMs + which don't support video should leave out the first class and + omit <tp:member-ref>InitialVideo</tp:member-ref> from the second + class, and vice versa for CMs without audio support.</p> + + <p>Handlers should not close <tp:dbus-ref + namespace="ofdT.Channel.Type">Call.DRAFT</tp:dbus-ref> channels + without first calling <tp:member-ref>Hangup</tp:member-ref> on + the channel. If a Call handler crashes, the <tp:dbus-ref + namespace="ofdT">ChannelDispatcher</tp:dbus-ref> will call + <tp:dbus-ref namespace="ofdT.Channel">Close</tp:dbus-ref> on the + channel which SHOULD also imply a call to + <tp:member-ref>Hangup</tp:member-ref>(<tp:type>Call_State_Change_Reason</tp:type>_User_Requested, + "org.freedesktop.Telepathy.Error.Terminated", "") before + actually closing the channel.</p> + + </tp:docstring> + + <method name="SetRinging" tp:name-for-bindings="Set_Ringing"> + <tp:changed version="0.21.2">renamed from Ringing</tp:changed> + <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="ofdT.Channel">Requested</tp:dbus-ref> + property is False, and + the <tp:member-ref>CallState</tp:member-ref> is + <tp:type>Call_State</tp:type>_Pending_Receiver (an incoming + call waiting on the local user to pick up). While this is + the case, this method SHOULD change the + <tp:member-ref>CallFlags</tp:member-ref> to include + <tp:type>Call_Flags</tp:type>_Locally_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="ofdT.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 + <tp:type>Call_State</tp:type>_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 + <tp:type>Call_State</tp:type>_Pending_Receiver, accept the + incoming call; this changes the + <tp:member-ref>CallState</tp:member-ref> to + <tp:type>Call_State</tp:type>_Accepted.</p> + + <p>For outgoing calls in state + <tp:type>Call_State</tp:type>_Pending_Initiator, actually + call the remote contact; this changes the + <tp:member-ref>CallState</tp:member-ref> to + <tp:type>Call_State</tp:type>_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="ofdT.Call" >Content.DRAFT</tp:dbus-ref> whose + <tp:dbus-ref namespace="ofdT.Call.Content.DRAFT" + >Disposition</tp:dbus-ref> is + <tp:type>Call_Content_Disposition</tp:type>_Initial, any + streams where the <tp:dbus-ref + namespace="ofdT.Call.Stream.DRAFT">LocalSendingState</tp:dbus-ref> + is <tp:type>Sending_State</tp:type>_Pending_Send will be + moved to <tp:type>Sending_State</tp:type>_Sending as if + <tp:dbus-ref namespace="ofdT.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. All contents will be removed + from the Call so that the + <tp:member-ref>Contents</tp:member-ref> property will be the + empty list. + </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> + Request that a new <tp:dbus-ref + namespace="ofdT.Call">Content.DRAFT</tp:dbus-ref> of type + Content_Type is added to the Call. Handlers should check the + value of the <tp:member-ref>MutableContents</tp:member-ref> + property before trying to add another content as it might not + be allowed. + </tp:docstring> + <arg direction="in" name="Content_Name" type="s"> + <tp:docstring> + <p>The suggested name of the content to add.</p> + + <tp:rationale> + The content name property should be meaningful, so should + be given a name which is significant to the user. The name + could be a localized "audio", "video" or perhaps include + some string identifying the source, such as a webcam + identifier. + </tp:rationale> + + <p>If there is already a content with the same name as this + property then a sensible suffix should be added. For example, + if this argument is "audio" but a content of the same name + already exists, a sensible suffix such as " (1)" is appended + to name the new content "audio (1)". A further content with the + name "audio" would then be named "audio (2)".</p> + + </tp:docstring> + </arg> + <arg direction="in" name="Content_Type" type="u" + tp:type="Media_Stream_Type"> + <tp:docstring> + The media stream type of the content to be added to the + call. + </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> + The media stream type given is invalid. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NotImplemented"> + <tp:docstring> + The media stream type requested is not implemented by the + CM. + </tp:docstring> + </tp:error> + <tp:error name="org.freedesktop.Telepathy.Error.NotCapable"> + <tp:docstring> + The content type requested cannot be added to this + call. Examples of why this might be the case include + because a second video stream cannot be added, or a + content cannot be added when the content set isn't + mutable. + </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="ofdT.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="ofdT.Call" + >Content.DRAFT</tp:dbus-ref> object. + </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="ofdT.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="ofdT.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="ofdT.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 <tp:type>Call_State</tp:type>_Pending_Receiver. It SHOULD + be set when <tp:member-ref>SetRinging</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 <tp:type>Call_State</tp:type>_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="ofdT.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 + <tp:type>Call_State</tp:type>_Pending_Receiver + or <tp:type>Call_State</tp:type>_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 + <tp:type>Call_State</tp:type>_Pending_Receiver or + <tp:type>Call_State</tp:type>_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 + <tp:type>Call_State</tp:type>_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="ofdT.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 + <tp:type>Call_State</tp:type>_Pending_Receiver or + <tp:type>Call_State</tp:type>_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 <tp:type>Call_State</tp:type>_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 + <tp:type>Call_Flags</tp:type>_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 + <tp:type>Call_State</tp:type>_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>Note that when in a conference call, this property is + purely to show your state in joining the call. The receiver + (or remote contact) in this context is the conference server + itself. The property does not change when other call members' + states change.</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> + + <p>When an ongoing call is active and not on hold or has any + other problems, this property will be 0.</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:enumvalue suffix="No_Answer" value="3"> + <tp:added version="0.21.2"/> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The <tp:member-ref>CallState</tp:member-ref> changed from + <tp:type>Call_State</tp:type>_Pending_Receiver to + <tp:type>Call_State</tp:type>_Ended because the initiator + ended the call before the receiver accepted it. With an + incoming call this state change reason signifies a missed + call.</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. If + <tp:type>Call_State_Change_Reason</tp:type>_User_Requested then + the Actor member will dictate whether it was the local user or + a remote contact responsible. + </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:immutable="yes"> + <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="ofdT.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:flag suffix="Conference_Host" value="4"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + This contact has merged this call into a conference. Note that GSM + provides a notification when the remote party merges a call into a + conference, but not when it is split out again; thus, this flag can + only indicate that the call has been part of a conference at some + point. If a GSM connection manager receives a notification that a + call has been merged into a conference a second time, it SHOULD + represent this by clearing and immediately re-setting this flag on + the remote contact. + </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"> + <p>A mapping from the remote contacts that are part of this call to flags + describing their status. This mapping never has the local user's handle + as a key.</p> + + <p>When the call ends, this property should be an empty list, + and notified with + <tp:member-ref>CallMembersChanged</tp:member-ref></p> + + <p>If the Call implements + <tp:dbus-ref namespace="ofdT.Channel.Interface" + >Group</tp:dbus-ref> and the Group members are + channel-specific handles, then this call SHOULD also use + channel-specific handles.</p> + + <p>Anonymous members are exposed as channel-specific handles + with no owner.</p> + </tp:docstring> + </property> + + <property name="InitialTransport" tp:name-for-bindings="Initial_Transport" + type="s" access="read" tp:requestable="yes" tp:immutable="yes"> + <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. Where not applicable, this property + is defined to be the empty string, in particular, on CMs with + hardware streaming.</p> + + <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> + </tp:docstring> + </property> + + <property name="InitialAudio" tp:name-for-bindings="Initial_Audio" + type="b" access="read" tp:immutable="yes" tp:requestable="yes"> + <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="ofdT.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="ofdT.Channel.Type.Call.DRAFT" + >Contents</tp:dbus-ref>).</p> + + <p>The name of this new content can be decided by using the + <tp:member-ref>InitialAudioName</tp:member-ref> property.</p> + + <p>Connection managers that support the <tp:dbus-ref + namespace="ofdT.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="ofdT.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="ofdT.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:immutable="yes" tp:requestable="yes"> + <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="InitialAudioName" tp:name-for-bindings="Initial_Audio_Name" + type="s" access="read" tp:immutable="yes" tp:requestable="yes"> + <tp:added version="0.21.2"/> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>If <tp:member-ref>InitialAudio</tp:member-ref> is set to + True, then this property will name the intial audio content + with the value of this property.</p> + + <tp:rationale> + <p>Content names are meant to be significant, but if no name + can be given to initial audio content, then its name cannot + be meaningful or even localized.</p> + </tp:rationale> + + <p>If this property is empty or missing from the channel + request and InitialAudio is True, then the CM must come up + with a sensible for the content, such as "audio".</p> + + <p>If the protocol has no concept of stream names then this + property will not show up in the allowed properties list of + the Requestable Channel Classes for call channels.</p> + </tp:docstring> + </property> + + <property name="InitialVideoName" tp:name-for-bindings="Initial_Video_Name" + type="s" access="read" tp:immutable="yes" tp:requestable="yes"> + <tp:added version="0.21.2"/> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The same as + <tp:member-ref>InitialAudioName</tp:member-ref>, but for a + video stream created by setting + <tp:member-ref>InitialVideo</tp:member-ref> to True. This + property is immutable and so cannot change.</p> + </tp:docstring> + </property> + + <property name="MutableContents" tp:name-for-bindings="Mutable_Contents" + type="b" access="read" tp:immutable="yes"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>If True, 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> + </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="ofdT.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref> + property is <tp:type>Stream_Transport_Type</tp: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="ofdT.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref> + property is <tp:type>Stream_Transport_Type</tp:type>_ICE.</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="ofdT.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref> + property is <tp:type>Stream_Transport_Type</tp:type>_WLM_2009.</p> + </tp:docstring> + </tp:hct> + + <tp:hct name="shm"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The client can implement streaming for streams whose <tp:dbus-ref + namespace="ofdT.Call.Stream.Interface.Media.DRAFT">Transport</tp:dbus-ref> + property is <tp:type>Stream_Transport_Type</tp:type>_SHM.</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="ofdT.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/telepathy-yell/Makefile.am b/telepathy-yell/Makefile.am new file mode 100644 index 0000000..dc71db1 --- /dev/null +++ b/telepathy-yell/Makefile.am @@ -0,0 +1,156 @@ +pkgincludedir = $(includedir)/telepathy-1.0/telepathy-yell +genincludedir = $(pkgincludedir)/_gen + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = telepathy-yell.pc + +EXTRA_DIST = \ + Call_Content_Codec_Offer.xml \ + Call_Content_Interface_Media.xml \ + Call_Content.xml \ + Call_Stream_Endpoint.xml \ + Call_Stream_Interface_Media.xml \ + Call_Stream.xml \ + Channel_Type_call.xml \ + call.xml + +lib_LTLIBRARIES = libtelepathy-yell.la + +libtelepathy_yell_la_LDFLAGS = -static +libtelepathy_yell_la_LIBADD = $(ALL_LIBS) + +pkginclude_HEADERS = \ + extensions.h + +libtelepathy_yell_la_SOURCES = \ + extensions.c \ + extensions-cli.c + +nodist_libtelepathy_yell_la_SOURCES = \ + _gen/signals-marshal.c \ + _gen/signals-marshal.h \ + _gen/signals-marshal.list \ + _gen/register-dbus-glib-marshallers-body.h \ + _gen/gtypes-body.h \ + _gen/interfaces-body.h \ + _gen/cli-call-body.h \ + _gen/svc-call.c + +nodist_geninclude_HEADERS = \ + _gen/enums.h \ + _gen/gtypes.h \ + _gen/interfaces.h \ + _gen/cli-call.h \ + _gen/svc-call.h + +BUILT_SOURCES = \ + _gen/all.xml \ + _gen/call.xml \ + $(nodist_geninclude_HEADERS) \ + $(nodist_libtelepathy_yell_la_SOURCES) \ + extensions.html + +CLEANFILES = $(BUILT_SOURCES) + +AM_CFLAGS = \ + -DG_LOG_DOMAIN=\"tp-yell\" \ + -I$(top_srcdir) -I$(top_builddir) \ + $(ERROR_CFLAGS) \ + $(TP_GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) + +ALL_LIBS = \ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(TP_GLIB_LIBS) + +distclean-local: + rm -rf _gen + +check_c_sources = \ + $(pkginclude_HEADERS) \ + $(libtelepathy_yell_la_SOURCES) +include $(top_srcdir)/tools/check-coding-style.mk + +check-local: check-coding-style + +### Code generation from here on down + +tools_dir = $(top_srcdir)/tools + +XSLTPROCFLAGS = --nonet --novalid + +# Generated files which can be generated for all categories simultaneously + +extensions.html: _gen/all.xml $(tools_dir)/doc-generator.xsl Makefile.am + $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) \ + --param 'allow-undefined-interfaces' 'true()' \ + $(tools_dir)/doc-generator.xsl \ + $< > $@ + +_gen/gtypes.h _gen/gtypes-body.h: _gen/all.xml \ + $(top_srcdir)/tools/glib-gtypes-generator.py Makefile.am + $(AM_V_GEN)$(PYTHON) $(top_srcdir)/tools/glib-gtypes-generator.py \ + $< _gen/gtypes Tpy + +_gen/signals-marshal.list: _gen/all.xml \ + $(tools_dir)/glib-signals-marshal-gen.py Makefile.am + $(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=_tpy_marshal $< > $@ + +_gen/signals-marshal.c: _gen/signals-marshal.list Makefile.am + $(AM_V_GEN){ echo '#include "_gen/signals-marshal.h"' && \ + $(GLIB_GENMARSHAL) --body --prefix=_tpy_marshal $< ; } > $@ + +_gen/register-dbus-glib-marshallers-body.h: _gen/all.xml \ + $(tools_dir)/glib-client-marshaller-gen.py Makefile.am + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-client-marshaller-gen.py $< \ + _tpy > $@ + +_gen/enums.h: _gen/all.xml $(tools_dir)/c-constants-gen.py Makefile.am + $(AM_V_GEN)$(PYTHON) $(tools_dir)/c-constants-gen.py Tpy $< _gen/enums + +_gen/interfaces-body.h _gen/interfaces.h: _gen/all.xml \ + $(tools_dir)/glib-interfaces-gen.py Makefile.am + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-interfaces-gen.py \ + Tpy _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 *.xml) + @$(mkdir_p) _gen + $(AM_V_GEN)$(XSLTPROC) $(XSLTPROCFLAGS) --xinclude $(tools_dir)/identity.xsl \ + $< > $@ + +_gen/cli-%-body.h _gen/cli-%.h: _gen/%.xml \ + $(tools_dir)/glib-client-gen.py Makefile.am + $(AM_V_GEN)set -e; \ + case "$*" in \ + call) \ + subclass="TpProxy"; \ + assert="TP_IS_PROXY"; \ + ;; \ + esac; \ + $(PYTHON) $(tools_dir)/glib-client-gen.py \ + --group=`echo $* | tr x- x_` \ + --subclass=$$subclass \ + --subclass-assert=$$assert \ + --tp-proxy-api=0.7.6 \ + --iface-quark-prefix=TPY_IFACE_QUARK \ + $< Tpy_Cli _gen/cli-$* + +_gen/svc-%.c _gen/svc-%.h: _gen/%.xml \ + $(tools_dir)/glib-ginterface-gen.py Makefile.am + $(AM_V_GEN)$(PYTHON) $(tools_dir)/glib-ginterface-gen.py \ + --filename=_gen/svc-$* \ + --signal-marshal-prefix=_tpy \ + --include='<telepathy-glib/dbus.h>' \ + --include='"_gen/signals-marshal.h"' \ + --not-implemented-func='tp_dbus_g_method_return_not_implemented' \ + --allow-unstable \ + $< Tpy_Svc_ diff --git a/telepathy-yell/all.xml b/telepathy-yell/all.xml new file mode 100644 index 0000000..3ff9708 --- /dev/null +++ b/telepathy-yell/all.xml @@ -0,0 +1,30 @@ +<tp:spec + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + xmlns:xi="http://www.w3.org/2001/XInclude"> + +<tp:title>Yell extensions to the Telepathy spec</tp:title> + +<xi:include href="call.xml"/> + +<tp:generic-types> + <tp:external-type name="Media_Stream_Type" type="u" + from="Telepathy specification (Channel.Type.StreamedMedia)"/> + <tp:external-type name="Media_Stream_State" type="u" + from="Telepathy specification (Channel.Type.StreamedMedia)"/> + <tp:external-type name="Contact_Handle" type="u" + from="Telepathy specification (Connection)"/> + <tp:external-type name="Socket_Address_IP" type="(sq)" + from="Telepathy specification (generic)"/> + <tp:external-type name="String_Variant_Map" type="a{sv}" + from="Telepathy specification (generic)"/> + <tp:external-type name="DBus_Error_Name" type="s" + from="Telepathy specification (generic)"/> + <tp:external-type name="Handle" type="u" + from="Telepathy specification (Connection)"/> + <tp:external-type name="DBus_Interface" type="s" + from="Telepathy specification (generic)"/> + <tp:external-type name="String_String_Map" type="a{ss}" + from="Telepathy specification (generic)"/> +</tp:generic-types> + +</tp:spec> diff --git a/telepathy-yell/call.xml b/telepathy-yell/call.xml new file mode 100644 index 0000000..ee04dbd --- /dev/null +++ b/telepathy-yell/call.xml @@ -0,0 +1,15 @@ +<tp:spec + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" + xmlns:xi="http://www.w3.org/2001/XInclude"> + +<tp:title>Call interfaces</tp:title> + +<xi:include href="Call_Content.xml" /> +<xi:include href="Call_Content_Codec_Offer.xml" /> +<xi:include href="Call_Content_Interface_Media.xml" /> +<xi:include href="Call_Stream.xml" /> +<xi:include href="Call_Stream_Endpoint.xml" /> +<xi:include href="Call_Stream_Interface_Media.xml" /> +<xi:include href="Channel_Type_Call.xml" /> + +</tp:spec> diff --git a/telepathy-yell/debug-internal.h b/telepathy-yell/debug-internal.h new file mode 100644 index 0000000..cdcb093 --- /dev/null +++ b/telepathy-yell/debug-internal.h @@ -0,0 +1,3 @@ +/* Minimal version of telepathy-glib's debug-internal.h */ + +#define DEBUG(fmt, ...) g_debug ("%s: " fmt, G_STRFUNC , ##__VA_ARGS__) diff --git a/telepathy-yell/extensions-cli.c b/telepathy-yell/extensions-cli.c new file mode 100644 index 0000000..076ef29 --- /dev/null +++ b/telepathy-yell/extensions-cli.c @@ -0,0 +1,33 @@ +#include "extensions.h" + +#include <telepathy-glib/channel.h> +#include <telepathy-glib/connection.h> +#include <telepathy-glib/proxy-subclass.h> + +#include "_gen/signals-marshal.h" + +/* include auto-generated stubs for client-specific code */ +#include "_gen/cli-call-body.h" +#include "_gen/register-dbus-glib-marshallers-body.h" + +static gpointer +tpy_cli_once (gpointer unused) +{ + _tpy_register_dbus_glib_marshallers (); + + tp_channel_init_known_interfaces (); + tp_connection_init_known_interfaces (); + + tp_proxy_or_subclass_hook_on_interface_add (TP_TYPE_PROXY, + tpy_cli_call_add_signals); + + return NULL; +} + +void +tpy_cli_init (void) +{ + static GOnce once = G_ONCE_INIT; + + g_once (&once, tpy_cli_once, NULL); +} diff --git a/telepathy-yell/extensions.c b/telepathy-yell/extensions.c new file mode 100644 index 0000000..eeda462 --- /dev/null +++ b/telepathy-yell/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/telepathy-yell/extensions.h b/telepathy-yell/extensions.h new file mode 100644 index 0000000..3e2bff9 --- /dev/null +++ b/telepathy-yell/extensions.h @@ -0,0 +1,21 @@ +#ifndef __TELEPATHY_YELL_EXTENSIONS_H__ +#define __TELEPATHY_YELL_EXTENSIONS_H__ + +#include <glib-object.h> +#include <telepathy-glib/channel.h> +#include <telepathy-glib/connection.h> + +#include <telepathy-yell/_gen/enums.h> +#include <telepathy-yell/_gen/cli-call.h> +#include <telepathy-yell/_gen/svc-call.h> + +G_BEGIN_DECLS + +#include <telepathy-yell/_gen/gtypes.h> +#include <telepathy-yell/_gen/interfaces.h> + +void tpy_cli_init (void); + +G_END_DECLS + +#endif diff --git a/telepathy-yell/telepathy-yell-uninstalled.pc.in b/telepathy-yell/telepathy-yell-uninstalled.pc.in new file mode 100644 index 0000000..b77f3d4 --- /dev/null +++ b/telepathy-yell/telepathy-yell-uninstalled.pc.in @@ -0,0 +1,12 @@ +prefix= +exec_prefix= +abs_top_srcdir=@abs_top_srcdir@ +abs_top_builddir=@abs_top_builddir@ + +Name: Telepathy Yell (uninstalled copy) +Description: Extra Call telepathy interfaces +Version: @VERSION@ +Requires: pkg-config >= 0.21 +Requires.private: dbus-glib-1 glib-2.0 gobject-2.0 telepathy-glib >= 0.7.3 +Libs: ${abs_top_builddir}/telepathy-yell/libtelepathy-yell.la +Cflags: -I${abs_top_srcdir} -I${abs_top_builddir} diff --git a/telepathy-yell/telepathy-yell.pc.in b/telepathy-yell/telepathy-yell.pc.in new file mode 100644 index 0000000..4608de0 --- /dev/null +++ b/telepathy-yell/telepathy-yell.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Telepathy Yell +Description: Extra Call telepathy interfaces +Version: @VERSION@ +Requires: dbus-glib-1 glib-2.0 gobject-2.0 telepathy-glib >= 0.7.3 +Libs: -L${libdir} -ltelepathy-yell +Cflags: -I${includedir}/telepathy-1.0 diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..363261d --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,53 @@ +EXTRA_DIST = \ + c-constants-gen.py \ + check-coding-style.mk \ + check-c-style.sh \ + check-misc.sh \ + check-whitespace.sh \ + doc-generator.xsl \ + glib-client-gen.py \ + glib-client-marshaller-gen.py \ + glib-errors-enum-body-gen.py \ + glib-errors-enum-header-gen.py \ + glib-ginterface-gen.py \ + glib-gtypes-generator.py \ + glib-interfaces-gen.py \ + glib-signals-marshal-gen.py \ + gobject-foo.py \ + identity.xsl \ + libtpcodegen.py \ + libglibcodegen.py \ + make-version-script.py \ + with-session-bus.sh + +CLEANFILES = libtpcodegen.pyc libtpcodegen.pyo libglibcodegen.pyc libglibcodegen.pyo $(noinst_SCRIPTS) + +all: $(EXTRA_DIST) + +libglibcodegen.py: libtpcodegen.py + touch $@ +c-constants-gen.py: libglibcodegen.py + touch $@ +glib-client-marshaller-gen.py: libglibcodegen.py + touch $@ +glib-errors-enum-body-gen.py: libglibcodegen.py + touch $@ +glib-errors-enum-header-gen.py: libglibcodegen.py + touch $@ +glib-ginterface-gen.py: libglibcodegen.py + touch $@ +glib-gtypes-generator.py: libglibcodegen.py + touch $@ +glib-interfaces-gen.py: libglibcodegen.py + touch $@ +glib-signals-marshal-gen.py: libglibcodegen.py + touch $@ + +TELEPATHY_GLIB_SRCDIR = $(top_srcdir)/../telepathy-glib +maintainer-update-from-telepathy-glib: + set -e && cd $(srcdir) && \ + for x in $(EXTRA_DIST); do \ + if test -f $(TELEPATHY_GLIB_SRCDIR)/tools/$$x; then \ + cp $(TELEPATHY_GLIB_SRCDIR)/tools/$$x $$x; \ + fi; \ + done diff --git a/tools/c-constants-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/check-c-style.sh b/tools/check-c-style.sh new file mode 100644 index 0000000..dd62fb7 --- /dev/null +++ b/tools/check-c-style.sh @@ -0,0 +1,56 @@ +#!/bin/sh +fail=0 + +( . "${tools_dir}"/check-misc.sh ) || fail=$? + +if grep -n '^ *GError *\*[[:alpha:]_][[:alnum:]_]* *;' "$@" +then + echo "^^^ The above files contain uninitialized GError*s - they should be" + echo " initialized to NULL" + fail=1 +fi + +# The first regex finds function calls like foo() (as opposed to foo ()). +# It attempts to ignore string constants (may cause false negatives). +# The second and third ignore block comments (gtkdoc uses foo() as markup). +# The fourth ignores cpp so you can +# #define foo(bar) (_real_foo (__FUNC__, bar)) (cpp insists on foo() style). +if grep -n '^[^"]*[[:lower:]](' "$@" \ + | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *\*' \ + | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: */\*' \ + | grep -v '^[-[:alnum:]_./]*:[[:digit:]]*: *#' +then + echo "^^^ Our coding style is to use function calls like foo (), not foo()" + fail=1 +fi + +if grep -En '[(][[:alnum:]_]+ ?\*[)][(]?[[:alpha:]_]' "$@"; then + echo "^^^ Our coding style is to have a space between a cast and the " + echo " thing being cast" + fail=1 +fi + +# this only spots casts +if grep -En '[(][[:alnum:]_]+\*+[)]' "$@"; then + echo "^^^ Our coding style is to have a space before the * of pointer types" + echo " (regex 1)" + fail=1 +fi +# ... and this only spots variable declarations and function return types +if grep -En '^ *(static |const |)* *[[:alnum:]_]+\*+([[:alnum:]_]|;|$)' \ + "$@"; then + echo "^^^ Our coding style is to have a space before the * of pointer types" + echo " (regex 2)" + fail=1 +fi + +if test -n "$CHECK_FOR_LONG_LINES" +then + if egrep -n '.{80,}' "$@" + then + echo "^^^ The above files contain long lines" + fail=1 + fi +fi + +exit $fail diff --git a/tools/check-coding-style.mk b/tools/check-coding-style.mk new file mode 100644 index 0000000..1c0a60f --- /dev/null +++ b/tools/check-coding-style.mk @@ -0,0 +1,17 @@ +check-coding-style: + @fail=0; \ + if test -n "$(check_misc_sources)"; then \ + tools_dir=$(top_srcdir)/tools \ + sh $(top_srcdir)/tools/check-misc.sh \ + $(addprefix $(srcdir)/,$(check_misc_sources)) || fail=1; \ + fi; \ + if test -n "$(check_c_sources)"; then \ + tools_dir=$(top_srcdir)/tools \ + sh $(top_srcdir)/tools/check-c-style.sh \ + $(addprefix $(srcdir)/,$(check_c_sources)) || fail=1; \ + fi;\ + if test yes = "$(ENABLE_CODING_STYLE_CHECKS)"; then \ + exit "$$fail";\ + else \ + exit 0;\ + fi diff --git a/tools/check-misc.sh b/tools/check-misc.sh new file mode 100644 index 0000000..89e8e87 --- /dev/null +++ b/tools/check-misc.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +fail=0 + +( . "${tools_dir}"/check-whitespace.sh ) || fail=$? + +if egrep '(Free\s*Software\s*Foundation.*02139|02111-1307)' "$@" +then + echo "^^^ The above files contain the FSF's old address in GPL headers" + fail=1 +fi + +exit $fail diff --git a/tools/check-whitespace.sh b/tools/check-whitespace.sh new file mode 100644 index 0000000..5348331 --- /dev/null +++ b/tools/check-whitespace.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +fail=0 + +if grep -n ' $' "$@" +then + echo "^^^ The above files contain unwanted trailing spaces" + fail=1 +fi + +if grep -n ' ' "$@" +then + echo "^^^ The above files contain tabs" + fail=1 +fi + +exit $fail diff --git a/tools/doc-generator.xsl b/tools/doc-generator.xsl new file mode 100644 index 0000000..76fc969 --- /dev/null +++ b/tools/doc-generator.xsl @@ -0,0 +1,1199 @@ +<!-- Generate HTML documentation from the Telepathy specification. +The master copy of this stylesheet is in the Telepathy spec 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 +--> + +<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"> + <!--Don't move the declaration of the HTML namespace up here — XMLNSs + don't work ideally in the presence of two things that want to use the + absence of a prefix, sadly. --> + + <xsl:param name="allow-undefined-interfaces" select="false()"/> + + <xsl:template match="html:* | @*" mode="html"> + <xsl:copy> + <xsl:apply-templates mode="html"/> + </xsl:copy> + </xsl:template> + + <xsl:template match="tp:type" mode="html"> + <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" mode="html"> + <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')] + "> + <a xmlns="http://www.w3.org/1999/xhtml" href="#{$name}"> + <xsl:value-of select="string(.)"/> + </a> + </xsl:when> + + <xsl:when test="$allow-undefined-interfaces"> + <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>' </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" mode="html"> + <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> <interface> </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> </xsl:text> + </xsl:message> + </xsl:otherwise> + </xsl:choose> + + <a xmlns="http://www.w3.org/1999/xhtml" href="#{$prefix}{$name}"> + <xsl:value-of select="$name"/> + </a> + </xsl:template> + + <xsl:template match="*" mode="identity"> + <xsl:copy> + <xsl:apply-templates mode="identity"/> + </xsl:copy> + </xsl:template> + + <xsl:template match="tp:docstring"> + <xsl:apply-templates mode="html"/> + </xsl:template> + + <xsl:template match="tp:added"> + <p class="added" xmlns="http://www.w3.org/1999/xhtml">Added in + version <xsl:value-of select="@version"/>. + <xsl:apply-templates select="node()" mode="html"/></p> + </xsl:template> + + <xsl:template match="tp:changed"> + <xsl:choose> + <xsl:when test="node()"> + <p class="changed" xmlns="http://www.w3.org/1999/xhtml">Changed in + version <xsl:value-of select="@version"/>: + <xsl:apply-templates select="node()" mode="html"/></p> + </xsl:when> + <xsl:otherwise> + <p class="changed">Changed in version + <xsl:value-of select="@version"/></p> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="tp:deprecated"> + <p class="deprecated" xmlns="http://www.w3.org/1999/xhtml">Deprecated + since version <xsl:value-of select="@version"/>. + <xsl:apply-templates select="node()" mode="html"/></p> + </xsl:template> + + <xsl:template match="tp:rationale" mode="html"> + <div xmlns="http://www.w3.org/1999/xhtml" class="rationale"> + <xsl:apply-templates select="node()" mode="html"/> + </div> + </xsl:template> + + <xsl:template match="tp:errors"> + <h1 xmlns="http://www.w3.org/1999/xhtml">Errors</h1> + <xsl:apply-templates/> + </xsl:template> + + <xsl:template match="tp:generic-types"> + <h1 xmlns="http://www.w3.org/1999/xhtml">Generic types</h1> + <xsl:call-template name="do-types"/> + </xsl:template> + + <xsl:template name="do-types"> + <xsl:if test="tp:simple-type"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Simple types</h2> + <xsl:apply-templates select="tp:simple-type"/> + </xsl:if> + + <xsl:if test="tp:enum"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Enumerated types:</h2> + <xsl:apply-templates select="tp:enum"/> + </xsl:if> + + <xsl:if test="tp:flags"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Sets of flags:</h2> + <xsl:apply-templates select="tp:flags"/> + </xsl:if> + + <xsl:if test="tp:struct"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Structure types</h2> + <xsl:apply-templates select="tp:struct"/> + </xsl:if> + + <xsl:if test="tp:mapping"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Mapping types</h2> + <xsl:apply-templates select="tp:mapping"/> + </xsl:if> + + <xsl:if test="tp:external-type"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Types defined elsewhere</h2> + <dl><xsl:apply-templates select="tp:external-type"/></dl> + </xsl:if> + </xsl:template> + + <xsl:template match="tp:error"> + <h2 xmlns="http://www.w3.org/1999/xhtml"><a name="{concat(../@namespace, '.', translate(@name, ' ', ''))}"></a><xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/></h2> + <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:spec/tp:copyright"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates mode="text"/> + </div> + </xsl:template> + <xsl:template match="/tp:spec/tp:license"> + <div xmlns="http://www.w3.org/1999/xhtml" class="license"> + <xsl:apply-templates mode="html"/> + </div> + </xsl:template> + + <xsl:template match="tp:copyright"/> + <xsl:template match="tp:license"/> + + <xsl:template match="interface"> + <h1 xmlns="http://www.w3.org/1999/xhtml"><a name="{@name}"></a><xsl:value-of select="@name"/></h1> + + <xsl:if test="@tp:causes-havoc"> + <p xmlns="http://www.w3.org/1999/xhtml" class="causes-havoc"> + 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. + </p> + </xsl:if> + + <xsl:if test="tp:requires"> + <p>Implementations of this interface must also implement:</p> + <ul xmlns="http://www.w3.org/1999/xhtml"> + <xsl:for-each select="tp:requires"> + <li><code><a href="#{@interface}"><xsl:value-of select="@interface"/></a></code></li> + </xsl:for-each> + </ul> + </xsl:if> + + <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="method"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Methods:</h2> + <xsl:apply-templates select="method"/> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no methods.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:choose> + <xsl:when test="signal"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Signals:</h2> + <xsl:apply-templates select="signal"/> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no signals.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:choose> + <xsl:when test="tp:property"> + <h2 xmlns="http://www.w3.org/1999/xhtml">Telepathy Properties:</h2> + <p xmlns="http://www.w3.org/1999/xhtml">Accessed using the + <a href="#org.freedesktop.Telepathy.Properties">Telepathy + Properties</a> interface.</p> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:property"/> + </dl> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no Telepathy + properties.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:choose> + <xsl:when test="property"> + <h2 xmlns="http://www.w3.org/1999/xhtml">D-Bus core Properties:</h2> + <p xmlns="http://www.w3.org/1999/xhtml">Accessed using the + org.freedesktop.DBus.Properties interface.</p> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="property"/> + </dl> + </xsl:when> + <xsl:otherwise> + <p xmlns="http://www.w3.org/1999/xhtml">Interface has no D-Bus core + properties.</p> + </xsl:otherwise> + </xsl:choose> + + <xsl:call-template name="do-types"/> + + </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 </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> </xsl:text> + </xsl:message> + </xsl:if> + + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> + </h3> + <xsl:apply-templates select="tp:docstring" /> + <xsl:apply-templates select="tp:added"/> + <xsl:apply-templates select="tp:changed"/> + <xsl:apply-templates select="tp:deprecated"/> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <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"> + <dt xmlns="http://www.w3.org/1999/xhtml"><code><xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/></code></dt> + <xsl:choose> + <xsl:when test="tp:docstring"> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring" /> + <xsl:apply-templates select="tp:added"/> + <xsl:apply-templates select="tp:changed"/> + <xsl:apply-templates select="tp:deprecated"/> + </dd> + </xsl:when> + <xsl:otherwise> + <dd xmlns="http://www.w3.org/1999/xhtml">(Undocumented)</dd> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </dl> + </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 </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> </xsl:text> + </xsl:message> + </xsl:if> + + <h3 xmlns="http://www.w3.org/1999/xhtml"> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> + </h3> + <xsl:apply-templates select="tp:docstring" /> + <xsl:apply-templates select="tp:added"/> + <xsl:apply-templates select="tp:changed"/> + <xsl:apply-templates select="tp:deprecated"/> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <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"> + <dt xmlns="http://www.w3.org/1999/xhtml"><code><xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/></code></dt> + <xsl:choose> + <xsl:when test="tp:docstring"> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring" /> + <xsl:apply-templates select="tp:added"/> + <xsl:apply-templates select="tp:changed"/> + <xsl:apply-templates select="tp:deprecated"/> + </dd> + </xsl:when> + <xsl:otherwise> + <dd xmlns="http://www.w3.org/1999/xhtml">(Undocumented)</dd> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </dl> + </xsl:template> + + <xsl:template match="property"> + + <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 </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> </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>' </xsl:text> + </xsl:message> + </xsl:if> + + <dt xmlns="http://www.w3.org/1999/xhtml"> + <a name="{concat(../@name, '.', @name)}"> + <code><xsl:value-of select="@name"/></code> + </a> + <xsl:text> − </xsl:text> + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + <xsl:text>, </xsl:text> + <xsl:choose> + <xsl:when test="@access = 'read'"> + <xsl:text>read-only</xsl:text> + </xsl:when> + <xsl:when test="@access = 'write'"> + <xsl:text>write-only</xsl:text> + </xsl:when> + <xsl:when test="@access = 'readwrite'"> + <xsl:text>read/write</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>' </xsl:text> + </xsl:message> + </xsl:otherwise> + </xsl:choose> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring"/> + <xsl:apply-templates select="tp:added"/> + <xsl:apply-templates select="tp:changed"/> + <xsl:apply-templates select="tp:deprecated"/> + </dd> + </xsl:template> + + <xsl:template match="tp:property"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <xsl:if test="@name"> + <code><xsl:value-of select="@name"/></code> − + </xsl:if> + <code><xsl:value-of select="@type"/></code> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring"/> + <xsl:apply-templates select="tp:added"/> + <xsl:apply-templates select="tp:changed"/> + <xsl:apply-templates select="tp:deprecated"/> + </dd> + </xsl:template> + + <xsl:template match="tp:mapping"> + <div xmlns="http://www.w3.org/1999/xhtml" class="struct"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> − a{ + <xsl:for-each select="tp:member"> + <xsl:value-of select="@type"/> + <xsl:text>: </xsl:text> + <xsl:value-of select="@name"/> + <xsl:if test="position() != last()"> → </xsl:if> + </xsl:for-each> + } + </h3> + <div class="docstring"> + <xsl:apply-templates select="tp:docstring"/> + <xsl:if test="string(@array-name) != ''"> + <p>In bindings that need a separate name, arrays of + <xsl:value-of select="@name"/> should be called + <xsl:value-of select="@array-name"/>.</p> + </xsl:if> + </div> + <div> + <h4>Members</h4> + <dl> + <xsl:apply-templates select="tp:member" mode="members-in-docstring"/> + </dl> + </div> + </div> + </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 </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> </xsl:text> + </xsl:message> + </xsl:if> + + <div xmlns="http://www.w3.org/1999/xhtml" class="simple-type"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> − <xsl:value-of select="@type"/> + </h3> + <div class="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"/> + </div> + </div> + </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 </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> </xsl:text> + </xsl:message> + </xsl:if> + + <div xmlns="http://www.w3.org/1999/xhtml" class="external-type"> + <dt> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> − <xsl:value-of select="@type"/> + </dt> + <dd>Defined by: <xsl:value-of select="@from"/></dd> + </div> + </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()"> → </xsl:if> + </xsl:for-each> } + </xsl:template> + + <xsl:template match="tp:struct"> + <div xmlns="http://www.w3.org/1999/xhtml" class="struct"> + <h3> + <a name="type-{@name}"> + <xsl:value-of select="@name"/> + </a> − ( + <xsl:for-each select="tp:member"> + <xsl:value-of select="@type"/> + <xsl:text>: </xsl:text> + <xsl:value-of select="@name"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + ) + </h3> + <div class="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"/> + </div> + <xsl:choose> + <xsl:when test="string(@array-name) != ''"> + <p>In bindings that need a separate name, arrays of + <xsl:value-of select="@name"/> should be called + <xsl:value-of select="@array-name"/>.</p> + </xsl:when> + <xsl:otherwise> + <p>Arrays of <xsl:value-of select="@name"/> don't generally + make sense.</p> + </xsl:otherwise> + </xsl:choose> + <div> + <h4>Members</h4> + <dl> + <xsl:apply-templates select="tp:member" mode="members-in-docstring"/> + </dl> + </div> + </div> + </xsl:template> + + <xsl:template match="method"> + + <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 </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> </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'"> + <!-- FIXME: This is commented out until someone with a lot of time + on their hands goes through the spec adding names to all the "out" + arguments + + <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> + + <div xmlns="http://www.w3.org/1999/xhtml" class="method"> + <h3 xmlns="http://www.w3.org/1999/xhtml"> + <a name="{concat(../@name, concat('.', @name))}"> + <xsl:value-of select="@name"/> + </a> ( + <xsl:for-each xmlns="" select="arg[@direction='in']"> + <xsl:value-of select="@type"/>: <xsl:value-of select="@name"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + ) → + <xsl:choose> + <xsl:when test="arg[@direction='out']"> + <xsl:for-each xmlns="" select="arg[@direction='out']"> + <xsl:value-of select="@type"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + </xsl:when> + <xsl:otherwise>nothing</xsl:otherwise> + </xsl:choose> + </h3> + <div xmlns="http://www.w3.org/1999/xhtml" class="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"/> + </div> + + <xsl:if test="arg[@direction='in']"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Parameters</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="arg[@direction='in']" + mode="parameters-in-docstring"/> + </dl> + </div> + </xsl:if> + + <xsl:if test="arg[@direction='out']"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Returns</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="arg[@direction='out']" + mode="returns-in-docstring"/> + </dl> + </div> + </xsl:if> + + <xsl:if test="tp:possible-errors"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Possible errors</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:possible-errors/tp:error"/> + </dl> + </div> + </xsl:if> + + </div> + </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>' </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>' </xsl:text> + </xsl:message> + </xsl:if> + + <a href="#type-{$single-type}"><xsl:value-of select="$tp-type"/></a> + + </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="members-in-docstring"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <code><xsl:value-of select="@name"/></code> − + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:choose> + <xsl:when test="tp:docstring"> + <xsl:apply-templates select="tp:docstring" /> + </xsl:when> + <xsl:otherwise> + <em>(undocumented)</em> + </xsl:otherwise> + </xsl:choose> + </dd> + </xsl:template> + + <xsl:template match="arg" mode="parameters-in-docstring"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <code><xsl:value-of select="@name"/></code> − + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring" /> + </dd> + </xsl:template> + + <xsl:template match="arg" mode="returns-in-docstring"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <xsl:if test="@name"> + <code><xsl:value-of select="@name"/></code> − + </xsl:if> + <code><xsl:value-of select="@type"/></code> + <xsl:call-template name="parenthesized-tp-type"/> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="tp:docstring"/> + </dd> + </xsl:template> + + <xsl:template match="tp:possible-errors/tp:error"> + <dt xmlns="http://www.w3.org/1999/xhtml"> + <code><xsl:value-of select="@name"/></code> + </dt> + <dd xmlns="http://www.w3.org/1999/xhtml"> + <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"/> <em xmlns="http://www.w3.org/1999/xhtml">(generic description)</em> + </xsl:when> + <xsl:otherwise> + (Undocumented.) + </xsl:otherwise> + </xsl:choose> + </dd> + </xsl:template> + + <xsl:template match="signal"> + + <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 </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> </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'"> + <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> + + <div xmlns="http://www.w3.org/1999/xhtml" class="signal"> + <h3 xmlns="http://www.w3.org/1999/xhtml"> + <a name="{concat(../@name, concat('.', @name))}"> + <xsl:value-of select="@name"/> + </a> ( + <xsl:for-each xmlns="" select="arg"> + <xsl:value-of select="@type"/>: <xsl:value-of select="@name"/> + <xsl:if test="position() != last()">, </xsl:if> + </xsl:for-each> + )</h3> + + <div xmlns="http://www.w3.org/1999/xhtml" class="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"/> + </div> + + <xsl:if test="arg"> + <div xmlns="http://www.w3.org/1999/xhtml"> + <h4>Parameters</h4> + <dl xmlns="http://www.w3.org/1999/xhtml"> + <xsl:apply-templates select="arg" mode="parameters-in-docstring"/> + </dl> + </div> + </xsl:if> + </div> + </xsl:template> + + <xsl:output method="xml" indent="no" encoding="ascii" + omit-xml-declaration="yes" + doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" + doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" /> + + <xsl:template match="/tp:spec"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title> + <xsl:value-of select="tp:title"/> + <xsl:if test="tp:version"> + <xsl:text> version </xsl:text> + <xsl:value-of select="tp:version"/> + </xsl:if> + </title> + <style type="text/css"> + + body { + font-family: sans-serif; + margin: 2em; + height: 100%; + font-size: 1.2em; + } + h1 { + padding-top: 5px; + padding-bottom: 5px; + font-size: 1.6em; + background: #dadae2; + } + h2 { + font-size: 1.3em; + } + h3 { + font-size: 1.2em; + } + a:link, a:visited, a:link:hover, a:visited:hover { + font-weight: bold; + } + .topbox { + padding-top: 10px; + padding-left: 10px; + border-bottom: black solid 1px; + padding-bottom: 10px; + background: #dadae2; + font-size: 2em; + font-weight: bold; + color: #5c5c5c; + } + .topnavbox { + padding-left: 10px; + padding-top: 5px; + padding-bottom: 5px; + background: #abacba; + border-bottom: black solid 1px; + font-size: 1.2em; + } + .topnavbox a{ + color: black; + font-weight: normal; + } + .sidebar { + float: left; + /* width:9em; + border-right:#abacba solid 1px; + border-left: #abacba solid 1px; + height:100%; */ + border: #abacba solid 1px; + padding-left: 10px; + margin-left: 10px; + padding-right: 10px; + margin-right: 10px; + color: #5d5d5d; + background: #dadae2; + } + .sidebar a { + text-decoration: none; + border-bottom: #e29625 dotted 1px; + color: #e29625; + font-weight: normal; + } + .sidebar h1 { + font-size: 1.2em; + color: black; + } + .sidebar ul { + padding-left: 25px; + padding-bottom: 10px; + border-bottom: #abacba solid 1px; + } + .sidebar li { + padding-top: 2px; + padding-bottom: 2px; + } + .sidebar h2 { + font-style:italic; + font-size: 0.81em; + padding-left: 5px; + padding-right: 5px; + font-weight: normal; + } + .date { + font-size: 0.6em; + float: right; + font-style: italic; + } + .method, .signal, .property { + margin-left: 1em; + margin-right: 4em; + } + .rationale { + font-style: italic; + border-left: 0.25em solid #808080; + padding-left: 0.5em; + } + + .added { + color: #006600; + background: #ffffff; + } + .deprecated { + color: #ff0000; + background: #ffffff; + } + table, tr, td, th { + border: 1px solid #666; + } + + </style> + </head> + <body> + <h1 class="topbox"> + <xsl:value-of select="tp:title" /> + </h1> + <xsl:if test="tp:version"> + <h2>Version <xsl:value-of select="string(tp:version)"/></h2> + </xsl:if> + <xsl:apply-templates select="tp:copyright"/> + <xsl:apply-templates select="tp:license"/> + <xsl:apply-templates select="tp:docstring"/> + + <h2>Interfaces</h2> + <ul> + <xsl:for-each select="//node/interface"> + <li><code><a href="#{@name}"><xsl:value-of select="@name"/></a></code></li> + </xsl:for-each> + </ul> + + <xsl:apply-templates select="//node"/> + <xsl:apply-templates select="tp:generic-types"/> + <xsl:apply-templates select="tp:errors"/> + + <h1>Index</h1> + <h2>Index of interfaces</h2> + <ul> + <xsl:for-each select="//node/interface"> + <li><code><a href="#{@name}"><xsl:value-of select="@name"/></a></code></li> + </xsl:for-each> + </ul> + <h2>Index of types</h2> + <ul> + <xsl:for-each select="//tp:simple-type | //tp:enum | //tp:flags | //tp:mapping | //tp:struct | //tp:external-type"> + <xsl:sort select="@name"/> + <li> + <code> + <a href="#type-{@name}"> + <xsl:value-of select="@name"/> + </a> + </code> + <xsl:apply-templates mode="in-index" select="."/> + </li> + </xsl:for-each> + </ul> + </body> + </html> + </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>}}} </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> </xsl:text> + </xsl:message> + </xsl:template> +</xsl:stylesheet> + +<!-- vim:set sw=2 sts=2 et: --> 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..801eec1 --- /dev/null +++ b/tools/glib-client-marshaller-gen.py @@ -0,0 +1,62 @@ +#!/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 %s_register_dbus_glib_marshallers (void);' % self.prefix + print '' + + print 'void' + print '%s_register_dbus_glib_marshallers (void)' % self.prefix + print '{' + + all = self.marshallers.keys() + all.sort() + for marshaller in all: + rhs = self.marshallers[marshaller] + + print ' dbus_g_object_register_marshaller (%s,' % marshaller + print ' G_TYPE_NONE, /* return */' + for type in rhs: + print ' G_TYPE_%s,' % type.replace('VOID', 'NONE') + print ' G_TYPE_INVALID);' + + print '}' + + +def types_to_gtypes(types): + return [type_to_gtype(t)[1] for t in types] + +if __name__ == '__main__': + argv = sys.argv[1:] + dom = xml.dom.minidom.parse(argv[0]) + + Generator(dom, argv[1])() diff --git a/tools/glib-errors-enum-body-gen.py b/tools/glib-errors-enum-body-gen.py new file mode 100644 index 0000000..44863ee --- /dev/null +++ b/tools/glib-errors-enum-body-gen.py @@ -0,0 +1,62 @@ +#!/usr/bin/python + +import sys +import xml.dom.minidom + +from libglibcodegen import NS_TP, camelcase_to_upper, get_docstring, \ + get_descendant_text + +class Generator(object): + def __init__(self, dom): + self.dom = dom + self.errors = self.dom.getElementsByTagNameNS(NS_TP, 'errors')[0] + + def do_header(self): + print '/* Generated from the Telepathy spec\n' + copyrights = self.errors.getElementsByTagNameNS(NS_TP, 'copyright') + for copyright in copyrights: + print get_descendant_text(copyright) + license = self.errors.getElementsByTagNameNS(NS_TP, 'license')[0] + print '\n' + get_descendant_text(license) + '\n*/' + + def do_enum_values(self): + for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): + print '' + nick = error.getAttribute('name').replace(' ', '') + name = camelcase_to_upper(nick.replace('.', '')) + ns = error.parentNode.getAttribute('namespace') + enum = 'TP_ERROR_' + name + print ' /* ' + ns + '.' + name + print ' ' + get_docstring(error) + print ' */' + print ' { %s, "%s", "%s" },' % (enum, enum, nick) + + + def do_get_type(self): + print """ +#include <_gen/telepathy-errors.h> + +GType +tp_error_get_type (void) +{ + static GType etype = 0; + if (G_UNLIKELY (etype == 0)) + { + static const GEnumValue values[] = {""" + self.do_enum_values() + print """\ + }; + + etype = g_enum_register_static ("TpError", values); + } + return etype; +} +""" + + def __call__(self): + self.do_header() + self.do_get_type() + +if __name__ == '__main__': + argv = sys.argv[1:] + Generator(xml.dom.minidom.parse(argv[0]))() diff --git a/tools/glib-errors-enum-header-gen.py b/tools/glib-errors-enum-header-gen.py new file mode 100644 index 0000000..64939b4 --- /dev/null +++ b/tools/glib-errors-enum-header-gen.py @@ -0,0 +1,75 @@ +#!/usr/bin/python + +import sys +import xml.dom.minidom + +from libglibcodegen import NS_TP, camelcase_to_upper, get_docstring, \ + get_descendant_text + +class Generator(object): + def __init__(self, dom): + self.dom = dom + self.errors = self.dom.getElementsByTagNameNS(NS_TP, 'errors')[0] + + def do_header(self): + print '/* Generated from the Telepathy spec\n' + copyrights = self.errors.getElementsByTagNameNS(NS_TP, 'copyright') + for copyright in copyrights: + print get_descendant_text(copyright) + license = self.errors.getElementsByTagNameNS(NS_TP, 'license')[0] + print '\n' + get_descendant_text(license) + '\n*/' + + def do_gtkdoc(self): + for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): + ns = error.parentNode.getAttribute('namespace') + nick = error.getAttribute('name').replace(' ', '') + enum = 'TP_ERROR_' + camelcase_to_upper(nick.replace('.', '')) + print ' * @' + enum + ': ' + ns + '.' + nick + ':' + print ' * ' + get_docstring(error) + ' ' + + def do_enumnames(self): + for error in self.errors.getElementsByTagNameNS(NS_TP, 'error'): + nick = error.getAttribute('name').replace(' ', '') + enum = 'TP_ERROR_' + camelcase_to_upper(nick.replace('.', '')) + print ' ' + enum + ',' + + def do_get_type(self): + print """ +#include <glib-object.h> + +G_BEGIN_DECLS + +GType tp_error_get_type (void); + +/** + * TP_TYPE_ERROR: + * + * The GType of the Telepathy error enumeration. + */ +#define TP_TYPE_ERROR (tp_error_get_type()) +""" + + def do_enum(self): + print """\ +/** + * TpError:""" + self.do_gtkdoc() + print """\ + * + * Enumerated type representing the Telepathy D-Bus errors. + */ +typedef enum {""" + self.do_enumnames() + print """\ +} TpError; + +G_END_DECLS""" + + def __call__(self): + self.do_header() + self.do_get_type() + self.do_enum() + +if __name__ == '__main__': + argv = sys.argv[1:] + Generator(xml.dom.minidom.parse(argv[0]))() 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_##x (\\' + % (self.prefix_, self.node_name_lc)) + self.d(' * klass, my_object_##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/gobject-foo.py b/tools/gobject-foo.py new file mode 100644 index 0000000..5921cab --- /dev/null +++ b/tools/gobject-foo.py @@ -0,0 +1,81 @@ +#!/usr/bin/python + +# gobject-foo.py: generate standard GObject type macros etc. +# +# The master copy of this program is in the telepathy-glib repository - +# please make any changes there. +# +# Copyright (C) 2007 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 + +def gobject_header(head, tail, as_interface=False): + out = [] + o = out.append + + name = head + '_' + tail + MixedCase = name.replace('_', '') + lower_case = name.lower() + UPPER_CASE = name.upper() + + gtype = head.upper() + '_TYPE_' + tail.upper() + + o("typedef struct _%s %s;" % (MixedCase, MixedCase)) + o("typedef struct _%sClass %sClass;" % (MixedCase, MixedCase)) + o("typedef struct _%sPrivate %sPrivate;" % (MixedCase, MixedCase)) + o("") + o("GType %s_get_type (void);" % lower_case) + o("") + + o("#define %s \\" % gtype) + o(" (%s_get_type ())" % lower_case) + + o("#define %s(obj) \\" % UPPER_CASE) + o(" (G_TYPE_CHECK_INSTANCE_CAST ((obj), %s, \\" % gtype) + o(" %s))" % MixedCase) + + if not as_interface: + o("#define %s_CLASS(klass) \\" % UPPER_CASE) + o(" (G_TYPE_CHECK_CLASS_CAST ((klass), %s, \\" % gtype) + o(" %sClass))" % MixedCase) + + o("#define %s_IS_%s(obj) \\" % (head.upper(), tail.upper())) + o(" (G_TYPE_CHECK_INSTANCE_TYPE ((obj), %s))" % gtype) + + if not as_interface: + o("#define %s_IS_%s_CLASS(klass) \\" % (head.upper(), tail.upper())) + o(" (G_TYPE_CHECK_CLASS_TYPE ((klass), %s))" % gtype) + + o("#define %s_GET_CLASS(obj) \\" % UPPER_CASE) + o(" (G_TYPE_INSTANCE_GET_CLASS ((obj), %s, \\" % gtype) + o(" %sClass))" % MixedCase) + + return out + +if __name__ == '__main__': + import sys + from getopt import gnu_getopt + + options, argv = gnu_getopt(sys.argv[1:], '', ['interface']) + + as_interface = False + + for opt, val in options: + if opt == '--interface': + as_interface = True + + head, tail = argv + + print '\n'.join(gobject_header(head, tail, as_interface=as_interface)) diff --git a/tools/identity.xsl b/tools/identity.xsl new file mode 100644 index 0000000..6630f84 --- /dev/null +++ b/tools/identity.xsl @@ -0,0 +1,7 @@ +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:template match="@*|node()"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> +</xsl:stylesheet> 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('&', '&').replace("'", ''').replace('"', '"') + return s.replace('<', '<').replace('>', '>') diff --git a/tools/make-version-script.py b/tools/make-version-script.py new file mode 100644 index 0000000..0d30aa3 --- /dev/null +++ b/tools/make-version-script.py @@ -0,0 +1,208 @@ +#!/usr/bin/python + +"""Construct a GNU ld or Debian dpkg version-script from a set of +RFC822-style symbol lists. + +Usage: + make-version-script.py [--symbols SYMBOLS] [--unreleased-version VER] + [--dpkg "LIBRARY.so.0 LIBRARY0 #MINVER#"] + [--dpkg-build-depends-package LIBRARY-dev] + [FILES...] + +Each FILE starts with RFC822-style headers "Version:" (the name of the +symbol version, e.g. FOO_1.2.3) and "Extends:" (either the previous +version, or "-" if this is the first version). Next there is a blank +line, then a list of C symbols one per line. + +Comments (lines starting with whitespace + "#") are allowed and ignored. + +If --symbols is given, SYMBOLS lists the symbols actually exported by +the library (one per line). If --unreleased-version is given, any symbols +in SYMBOLS but not in FILES are assigned to that version; otherwise, any +such symbols cause an error. + +If --dpkg is given, produce a Debian dpkg-gensymbols file instead of a +GNU ld version-script. The argument to --dpkg is the first line of the +resulting symbols file, and --dpkg-build-depends-package can optionally +be used to set the Build-Depends-Package field. + +This script originates in telepathy-glib <http://telepathy.freedesktop.org/> - +please send us any changes that are needed. +""" + +# Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/> +# Copyright (C) 2008 Nokia Corporation +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. + +import sys +from getopt import gnu_getopt + + +def e(format, *args): + sys.stderr.write((format + '\n') % args) + + +def main(abifiles, symbols=None, unreleased_version=None, + dpkg=False, dpkg_first_line=None, dpkg_build_depends_package=None): + + gnuld = not dpkg + symbol_set = None + + if symbols is not None: + symbol_set = open(symbols, 'r').readlines() + symbol_set = map(str.strip, symbol_set) + symbol_set = set(symbol_set) + + versioned_symbols = set() + + dpkg_symbols = [] + dpkg_versions = [] + + if dpkg: + assert dpkg_first_line is not None + print dpkg_first_line + if dpkg_build_depends_package is not None: + print "* Build-Depends-Package: %s" % dpkg_build_depends_package + + for filename in abifiles: + lines = open(filename, 'r').readlines() + + version = None + extends = None + release = None + + for i, line in enumerate(lines): + line = line.strip() + + if line.startswith('#'): + continue + elif not line: + # the transition betwen headers and symbols + cut = i + 1 + break + elif line.lower().startswith('version:'): + line = line[8:].strip() + version = line + continue + elif line.lower().startswith('extends:'): + line = line[8:].strip() + extends = line + continue + elif line.lower().startswith('release:'): + release = line[8:].strip() + continue + else: + e('Could not understand line in %s header: %s', filename, line) + raise SystemExit(1) + + else: + e('No symbols in %s', filename) + raise SystemExit(1) + + if version is None: + e('No Versions: header in %s', filename) + raise SystemExit(1) + + if extends is None: + e('No Extends: header in %s', filename) + raise SystemExit(1) + + if release is None and dpkg: + e('No Release: header in %s', filename) + raise SystemExit(1) + + if dpkg: + dpkg_versions.append('%s@%s %s' % (version, version, release)) + + lines = lines[cut:] + + if gnuld: + print "%s {" % version + print " global:" + + for symbol in lines: + symbol = symbol.strip() + + if symbol.startswith('#'): + continue + + if gnuld: + print " %s;" % symbol + elif dpkg: + dpkg_symbols.append('%s@%s %s' % (symbol, version, release)) + + if symbol in versioned_symbols: + raise AssertionError('Symbol %s is in version %s and an ' + 'earlier version' % (symbol, version)) + + versioned_symbols.add(symbol) + + if gnuld: + if extends == '-': + print " local:" + print " *;" + print "};" + else: + print "} %s;" % extends + print + + if dpkg: + dpkg_symbols.sort() + dpkg_versions.sort() + + for x in dpkg_versions: + print " %s" % x + + for x in dpkg_symbols: + print " %s" % x + + if symbol_set is not None: + missing = versioned_symbols - symbol_set + + if missing: + e('These symbols have disappeared:') + + for symbol in missing: + e(' %s', symbol) + + raise SystemExit(1) + + unreleased = symbol_set - versioned_symbols + + if unreleased: + if unreleased_version is None: + e('Unversioned symbols are not allowed in releases:') + + for symbol in unreleased: + e(' %s', symbol) + + raise SystemExit(1) + + if gnuld: + print "%s {" % unreleased_version + print " global:" + + for symbol in unreleased: + print " %s;" % symbol + + print "} %s;" % version + + +if __name__ == '__main__': + options, argv = gnu_getopt (sys.argv[1:], '', + ['symbols=', 'unreleased-version=', + 'dpkg=', 'dpkg-build-depends-package=']) + + opts = {'dpkg': False} + + for option, value in options: + if option == '--dpkg': + opts['dpkg'] = True + opts['dpkg_first_line'] = value + else: + opts[option.lstrip('-').replace('-', '_')] = value + + main(argv, **opts) diff --git a/tools/with-session-bus.sh b/tools/with-session-bus.sh new file mode 100644 index 0000000..063bd7e --- /dev/null +++ b/tools/with-session-bus.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# with-session-bus.sh - run a program with a temporary D-Bus session daemon +# +# The canonical location of this program is the telepathy-glib tools/ +# directory, please synchronize any changes with that copy. +# +# Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. + +set -e + +me=with-session-bus + +dbus_daemon_args="--print-address=5 --print-pid=6 --fork" +sleep=0 + +usage () +{ + echo "usage: $me [options] -- program [program_options]" >&2 + echo "Requires write access to the current directory." >&2 + echo "" >&2 + echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 + echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 + echo "The output of dbus-monitor is saved in $me-<pid>.dbus-monitor-logs" >&2 + exit 2 +} + +while test "z$1" != "z--"; do + case "$1" in + --sleep=*) + sleep="$1" + sleep="${sleep#--sleep=}" + shift + ;; + --session) + dbus_daemon_args="$dbus_daemon_args --session" + shift + ;; + --config-file=*) + # FIXME: assumes config file doesn't contain any special characters + dbus_daemon_args="$dbus_daemon_args $1" + shift + ;; + *) + usage + ;; + esac +done +shift +if test "z$1" = "z"; then usage; fi + +exec 5> $me-$$.address +exec 6> $me-$$.pid + +cleanup () +{ + pid=`head -n1 $me-$$.pid` + if test -n "$pid" ; then + echo "Killing temporary bus daemon: $pid" >&2 + kill -INT "$pid" + fi + rm -f $me-$$.address + rm -f $me-$$.pid +} + +trap cleanup INT HUP TERM +dbus-daemon $dbus_daemon_args + +{ echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 +{ echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 + +e=0 +DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" +export DBUS_SESSION_BUS_ADDRESS + +if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then + echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 + dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ + > $me-$$.dbus-monitor-logs 2>&1 & +fi + +"$@" || e=$? + +if test $sleep != 0; then + sleep $sleep +fi + +trap - INT HUP TERM +cleanup + +exit $e |