diff options
75 files changed, 17610 insertions, 0 deletions
diff --git a/.gbs.conf b/.gbs.conf new file mode 100644 index 0000000..6c76bde --- /dev/null +++ b/.gbs.conf @@ -0,0 +1,2 @@ +[general] +upstream_tag = AT_SPI2_ATK_2_12_0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f797c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +Makefile.in +*Makefile +ChangeLog +*.pyc +aclocal.m4 +autom4te.cache +config.h.in~ +config +configure @@ -0,0 +1,17 @@ + +Authors of AT-SPI D-Bus implementation +-------------------------------------- + +Mike Gorse <mgorse@suse.com> +Mark Doffman <mark.doffman@codethink.co.uk> + + +Authors of AT-SPI spec & implementation in CORBA +------------------------------------------------ + +Bill Haneman <bill.haneman@sun.com> +Marc Mulcahy <marc.mulchay@sun.com> +Michael Meeks <micheal@ximian.com> + +with contributions from: +Mark McLoughlin <mark@skynet.ie> @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + 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 Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the 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 a program 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. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + 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, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +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 compile 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) 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. + + c) 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. + + d) 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 source code 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 to +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 Library 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 Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. + +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! @@ -0,0 +1,31 @@ +Install procedure +----------------- + + % gzip -cd at-spi-1.9.0.tar.gz | tar xvf - # unpack the sources + % cd at-spi-1.9.0 # change to the toplevel directory + % ./configure # run the `configure' script + % make # build at-spi + + [ Become root if necessary ] + % make install # install at-spi + +Requirements +------------ + + GNU make + GNU autotools + + pkg-config + + python + + glib + gtk+ + gail + + dbus + dbus-glib + dbus-python + + X + XEVIE (Reccomended) diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..96801fc --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,4 @@ + +Mike Gorse <mgorse@novell.com> +Li Yuan <lee.yuan@oracle.com> +Mark Doffman <mark.doffman@codethink.co.uk> diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..713fec5 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS=droute atk-adaptor + +gtk_modulesdir = $(libdir)/gnome-settings-daemon-3.0/gtk-modules/ +gtk_modules_DATA = at-spi2-atk.desktop + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = atk-bridge-2.0.pc + +EXTRA_DIST = $(gtk_modules_DATA) atk-bridge-2.0.pc.in + @@ -0,0 +1,487 @@ +What's new in at-spi2-atk 2.11.92: + +* Bump minimum libdbus version needed, since we use a function + introduced in 1.5. + +What's new in at-spi2-atk 2.11.91: + +* Improve compatibility with at-spi2-registryd <= 2.10. + +* Collection: Fix an infinite loop when encountering a NULL parent + +What's new in at-spi2-atk 2.11.90: + +* Add AtspitableCell, tracking the new atk interface + +What's new in at-spi2-atk 2.11.5: + +* Allow sending of properties with events upon request (BGO#708695). + +What's new in at-spi2-atk 2.11.3: + + * Fix atspi_text_get_bounded_ranges + +* document: add support to current page and page count (BGO#719508). + +What's new in at-spi2-atk 2.11.2: + +* Add new roles and states to track atk (BGO#710464, BGO#712608, BGO#712609). + +What's new in at-spi2-atk 2.11.1: + +* Fix Contains method for components (BGO#710730). + +What's new in at-spi2-atk 2.9.92: + +* Properly clean up when timing out when motifying keystroke listeners + (BGO#707218). + +* Allow lists of attribute values when reading collection match rules + (BGO#700865). + +What's new in at-spi2-atk 2.9.90: + +* Implement GetStringAtOffset (BGO#705581). + +What's new in at-spi2-atk 2.9.5: + +* Fix another crash when we're initialized and shut down repeatedly. + +What's new in at-spi2-atk 2.9.4: + +* Fix a deadlock for apps that also register AT-SPI key event listeners + (ie, the Orca preferences dialog works again). + +What's new in at-spi2-atk 2.9.3: + +* Fix for BGO#681276 (reentrancy issues with gnome-shell). + +What's new in at-spi2-atk 2.9.2: + +* Fix a memory leak in the socket adaptor (BGO#696733). + +* Fix various DBusError leaks (BGO#698951). + +* Fix crash when a main loop is shut down repeatedly (BGO#699554). + +* Suppress a warning if an app returns NULL when referencing a child. + +What's new in at-spi2-atk 2.7.91: + +* Add Locale property (BGO#694368). + +What's new in at-spi2-atk 2.7.90: + +* Bump atk dependency to 2.7.5 (BGO#693189). + +What's new in at-spi2-atk 2.7.5: + +* Support ATSPI_ROLE_LEVEL_BAR to correspond to the new atk role. + +* Fix various compiler warnings. + +What's new in at-spi2-atk 2.7.3: + +* re-register if the registry goes away and later returns. + +* Stop using deprecated glib functions. + +* Fix a few memory leaks. + +What's new in at-spi2-atk 2.7.2: + +* atk-adaptor: don't emit a critical in case the bridge was not initialized + (BGO#684334). + +* Remove dbind (it was only used for the droute test). + +* Fix a crash in socket_embed_hook if spi_global_register is NULL. + +* [droute] Fix memory leak in path cleanup (BGO#688363). + +What's new in at-spi2-atk 2.7.1: + +* Remove the schema; it was only used to specify the location of the + atk-bridge library, which is now installed in the standard path. + +* Fix compiler warnings (BGO#678045). + +What's new in at-spi2-atk 2.6.0: + +* Fix some crashes in atk_bridge_adaptor_cleanup (BGO#684434). + +* When the cache is activated, register it on the main D-Bus connection. + +What's new in at-spi2-atk 2.5.92: + +* Fix atspi_hyperlink_get_uri (BGO#683182). + +* Only initialize the cache when an AT is running. + +* Fix various memory leaks (BGO#683979, BGO#684077) +What's new in at-spi2-atk 2.5.91: + +* Removed the gtk 3.0 module (BGO#678315). + +What's new in at-spi2-atk 2.5.90: + +* Don't leak a GError when option parsing fails (BGO#679296) + +* Rework some inefficient code when removing clients and events (BGO#679295, + BGO#679297). + +What's new in at-spi2-atk 2.5.5: + +* Implement GetLocalizedName for actions (BGO#680598) + +* Allow to build out of source directory (BGO#680280). + +What's new in at-spi2-atk 2.5.4: + + Fix for bug #679013 - AtspiCollection should be implemented for + all containers + +* Don't create a (non-readable) subdirectory for the socket when root. + +* Plug ref count leaks (BGO#679285). + +* Only create a directory and a socket when requested. + +* Only add items to the cache on children-changed if the children are + included in the event. + +* Only send PropertyChange signals used for caching, absent listeners. + +What's new in at-spi2-atk 2.5.3: + +* Create a library (libatk-bridge), currently with atk_bridge_adaptor_init() + and atk_bridge_adaptor_cleanup() functions, to facilitate linking directly + into, ie GTK+ 3. + +* Only register events at beginning if an AT is listening (BGO#678475). + +* Use XDG_RUNTIME_DIR to hold the socket, rather than a potentially secure + directory hard-coded under /tmp (BGO#678348). + +* Fix various compiler warnings and build errors. + +What's new in at-spi2-atk 2.5.2: + +* Fix for bug 677211 - The collection interface's MATCH_ANY fails for states + +What's new in at-spi2-atk 2.5.1: + +* Updated Norwegian Nynorsk translation + +What's new in at-spi2-atk 2.4.0: + +* Updated Hindi translation. + +What's new in at-spi2-atk 2.3.92: + +* Add Khmer and Malayalam translations. + +What's new in at-spi2-atk 2.3.91: + +* Have GetIndexInParent() return a signed int, per the spec. + +* Send a DoAction reply message before invoking atk (works around + atk_action_do_action potentially not returning right away for gtk). + + +What's new in at-spi2-atk 2.3.90: + +* Have GrabFocus return a bool, per the spec, rather than a uint32. + +* Fix a potential crash when emitting a signal if the ATK implementor + misbehaves. + +What's new in at-spi2-atk 2.3.5: + +* Remove the ability to set an accessible's name and description via AT-SPI + (it seems wrong to have been exposing this in the first place). + +* Fix for BGO#659967: some list API usage fixes. + +* Fix for BGO#663967: Don't use /a11y/ as a dconf path. + +* Fix for BGO#666371: possible crash when accessibles are created and + deleted in rapid succession; eg, in gnome-shell) + +* Avoid triggering GLib criticals in a few places. + +What's new in at-spi2-atk 2.3.4: + +* Fix for BGO#666870: Keystroke listeners do not work unless an event listener + is also registered [also needs updated at-spi2-core] + +What's new in at-spi2-atk 2.3.3: + +* Fix for BGO#664822 - gnome-shell crash when an AT is launched + +* Fix a reference leak if a child-added signal has no object value. + +* Change a : to a / in the suffix to an event, to allow DBus match rules + using arg0path. + +* Only deregister objects when they are marked defunct, not when a previously + defunct object is marked as no longer defunct. + +What's new in at-spi2-atk 2.3.2: + +* Have AtkSocket's implementation of ref_state_set to return empty sets + instead of NULL + +* Fix for BGO#663876: Make sure the a11y hierarchy under an AtkPlug is + generated when embedding. + +What's new in at-spi2-atk 2.3.1: + +* Fix a small coding error that could generate compiler warnings. + +What's new in at-spi2-atk 2.2.1: + +* Updated Finnish translation. + +What's new in at-spi2-atk 2.2.0: + +* Really fix BGO#658013: Attach the timeout for a key listener to the + appropriate main loop context. + +What's new in at-spi2-atk 2.1.92: + +* Fix for BGO#658013: Add timeout to check for disconnect on a key listener. + +What's new in at-spi2-atk 2.1.91: + +* Fix for BGO#645321: Use an array of bytes rather than a string for the + atk-bridge location in the schema. + +What's new in at-spi2-atk 2.1.90: + +* Try to use the new AtkWindow interface to register for window events. + +What's new in at-spi2-atk 2.1.5: + +* Use libatspi constants rather than keeping duplicate copies of the + constants in at-spi2-atk. + +* Fixed some problems when shutting down and restarting the module. + +What's new in at-spi2-atk 2.1.4: + +* Ensure the detail integers are initialized before sending events. + +* Map some new atk roles. + +What's new in at-spi2-atk 2.1.3: + +* Fix for BGO#652797: Remove unused AtkMisc instance. + +* Only register events when something is listening. In theory, this should + mitigate performance loss that might show up when no ATs are running. + +* BGO#652596: Allow setting value via the DBus property again. + +* BGO#652858: Deregister objects on state-changed:defunct. + +* Add AT-SPI mapping for ATK_RELATION_NODE_PARENT_OF. + +What's new in at-spi2-atk 2.0.2: + +* Fix matching on attributes for collection methods + +* Fix for BGO#650286: Ensure valid UTF-8 from ATK + +* Always emit children-changed, property-change, and state-changed events, in + order to keep caches synchronized. + +* Add GetAtspiVersion to fetch the version of the AT-SPI specification provided + by an application. + +What's new in at-spi2-atk 2.0.1: + +* Fix a memory leak in impl_GetText + +What's new in at-spi2-atk 1.91.93: + +* Removed dbus-glib-related includes, as they are no longer required. + +What's new in at-spi2-atk 1.91.92: + +* Handle text-insert and text-removed signals from ATK (BGO#638377). + +* Use the new dbus errors when compiled against a version of libdbus that + defines them. + +* Use libatspi to get the accessibility bus and handle main loop integration. + This fixes a crash with some builds of Firefox (FDO#35115). + Note that this adds a dependency on libatspi. + +* Fix accessibility of applications running as root on Linux. + +What's new in at-spi2-atk 1.91.91: + +* Fix some missing prototypes. + +* Do not exit if the accessibility bus disconnects. + +What's new in at-spi2-atk 1.91.90: + +* Fix for BGO#641338: Avoid crashing when unable to listen for p2p connections. + +* Set /tmp/at-spi2 to be world-writable. + +* Fix for BGO#641869: Remove --enable-relocate option. + +* Changed accessibility key name as was done in gsettings-desktop-schemas. + +* Fixed several memory leaks. + +* Fixed some build errors. + +What's new in at-spi2-atk 1.91.6: + +* Fixed the path in org.a11y.atspi.gschema.xml for lib64. + +* Implemented SetPosition, SetExtents, and SetSize for components. + +What's new in at-spi2-atk 1.91.5: + +* Fixed some memory leaks. + +* Fixed a crash if peer-to-peer connections are disabled. + +* Fixed setting of GTK_PATH with --enable-relocate (was broken in 1.91.4). + +What's new in at-spi2-atk 1.91.4: + +* Support direct dbus connections to improve performance if dbus-glib + 0.90 or greater is available. + +* Added a GSettings key to specify the location of libatk-bridge.so. + +* Added a desktop file to load gail and atk-bridge with the new + gnome-settings-daemon. + +What's new in at-spi2-atk 1.91.3: + +* Attributes in a collection match rule are now expected to be sent as a + dictionary. + +What's new in at-spi2-atk 1.91.2: + +* FIxed BGO#563546: Removed the g_atexit hook. + +What's new in at-spi2-atk 1.91.1: + + * Fire all events until we receive a reply from GetRegisteredEvents + +What's new in at-spi2-atk 1.91.0: + +* --disable-relocate is the default again. + +What's new in at-spi2-atk 0.3.92: + +* Have value methods return 0 on failure rather than fail, as in original pyatspi + +What's new in at-spi2-atk 0.3.91.1: + +* Fi a build error introduced in 0.3.91. + +What's new in at-spi2-atk 0.3.91: + +* Default to --enable-relocate for now. + +* Fix for FDO#29880: gtk module can't handle reloading. + +What's new in at-spi2-atk 0.3.90: + +* FDO#29365: Stop using a deprecated glib function. + +* Caching fixes--GetItems was completely broken in v0.3.6, and fixed a +possible crash when it is called. + +* Only emit signals when AT-SPI clients are listening for them. + +What's new in at-spi2-atk 0.3.6: + +* Fixed a problem with dbus introspection. + +* Do not block waiting for a response when registering. + +What's new in at-spi2-atk 0.3.5: + +* Have a socket retrieve its state set from its embedded plug + +* Rename AT_SPI_CLIENT to AT_SPI_REENTER_G_MAIN_LOOP, but still check the +former variable as well for now. + +What's new in at-spi2-atk 0.3.4: + +* Ref an object while adding it to the message generated by GetItems. +This prevents a crash if all other references to the objects go away while +it is being analyzed. + +* Add GetChildren to the introspection. + +* Fix NSelectedRows and NSelectedColumns. + +* Fix the behavior of ChildCount and GetChildAtIndex for sockets with +embedded plugs. + +* Fix extra unref when calling get_row_header or get_column_header on a +table, or calling atk_hyperlink_get_object. + +* Set the /desktop/gnome/interface/at-spi-dbus gconf key to false by default +(this key is only used when at-spi2 is relocated, which it is not by default). + +* Fix a few compiler warnings. + +What's new in at-spi2-atk 0.3.3: + +* Remove unused gtk build dependency. + +* Install a copy of the module into the gtk-3.0 modules directory. + +* Correct handling of some children-changed events sent by Firefox. + +* Lease objects that send events if they are not cached; fixes some +tracebacks when handling Firefox events. + +What's new in at-spi2-atk 0.3.2: + +* Some hyperlink fixes. + +* Cache additions are now done in an idle call. This prevents additions +from being made when an object may not be fully initialized and prevents +Firefox 3.6 from crashing. + +* The cache object has been placed into the org.a11y.atspi namespace. + +What's new in at-spi2-atk 0.3.1: + +* Added a gconf schema. + +* org.freedesktop.DBus.Properties.GetAll now works rather then crashing. + +* The position, size, and extents of an AtkSocket are now fetched from the +corresponding AtkPlug. + +* An application is no longer registered if its root is an AtkPlug. + +* Fix BGO#538680 - Count ignored in GetMatchesTo + +* Fix FDO#27626 - deadlock when registering an application. + +* Fix a problem with various hyperlink methods. + +* Fix for an AtkPlug sometimes not appearing in the hierarchy. + +* Fix a crash if the registry returns an unexpected message while embedding. + +* Remove libxml2 dependency since libxml2 is no longer used. + +* Fix handling of MATCH_ANY for a stateset in a collection match rule. + +* Disabled relocation by default. @@ -0,0 +1,86 @@ +D-Bus AT-SPI +------------ + +This version of at-spi is a major break from version 1.x. +It has been completely rewritten to use D-Bus rather than +ORBIT / CORBA for its transport protocol. + +A page including instructions for testing, project status and +TODO items is kept up to date at: + + http://www.linuxfoundation.org/en/AT-SPI_on_D-Bus + +The mailing list used for general questions is: + + accessibility-atspi@lists.linux-foundation.org + +For bug reports, feature requests, patches or enhancements please use +the AT-SPI project on bugzilla.gnome.org. Use the at-spi2-atk component for +bugs specific to this module. + + http://bugzilla.gnome.org + +A git repository with the latest development code is available at: + + git://git.gnome.org/at-spi2-atk + +Code in this repository depends on at-spi2-core resources. The +at-spi2-core repository can be found at: + + git://git.gnome.org/at-spi2-core + +More information +---------------- + +The project was started with a D-Bus performance review +the results of which are available on the GNOME wiki. Keep in +mind that the D-Bus AT-SPI design documents on this page +have not been kept up to date. + + http://live.gnome.org/GAP/AtSpiDbusInvestigation/ + +Other sources of relevant information about AT-SPI and Accessibility +include: + + http://live.gnome.org/Accessibility + http://www.sun.com/software/star/gnome/accessibility/architecture.xml + http://accessibility.kde.org/developer/atk.php + http://www.gnome.org/~billh/at-spi-idl/html/ + + + +Contents of this package +------------------------ + +This package includes libatk-bridge, a library that bridges ATK to the new +D-Bus based AT-SPI, as well as a corresponding module for gtk+ 2.x. Gtk+ 3.x +now links against libatk-bridge directly rather than requiring it to be loaded +as a module. + +These libraries depend on the at-spi2-core code that contains the daemon for +registering applications, D-Bus helper libraries and the AT-SPI D-Bus specifications. + +Directory structure +------------------- + +The directories within this package are arranged as follows: + + droute + + Contains a framework for registering objects + with a D-Bus connection and for routing messages to + the implementing object. + + Used by the ATK adaptor. + + atk-adaptor + + This directory contains code that bridges + the at-spi to the GTK+ toolkit, and which is + loaded at runtime by GTK+-based Gnome applications. + The 'bridge' automatically registers GTK+-2.0 + applications with the accessibility registry, + and relays UI events from application to registry. + It is also responsible for servicing requests from + the registry to register handlers for specific event + types. diff --git a/at-spi2-atk.desktop b/at-spi2-atk.desktop new file mode 100644 index 0000000..9b6d2fa --- /dev/null +++ b/at-spi2-atk.desktop @@ -0,0 +1,6 @@ +[GTK Module] +Name=AT-SPI2 ATK +Description=Accessibility ToolKit GTK+ Module +X-GTK-Module-Name=gail:atk-bridge +X-GTK-Module-Enabled-Schema=org.gnome.desktop.interface +X-GTK-Module-Enabled-Key=toolkit-accessibility diff --git a/at-spi2-atk.doap b/at-spi2-atk.doap new file mode 100644 index 0000000..52837e0 --- /dev/null +++ b/at-spi2-atk.doap @@ -0,0 +1,33 @@ +<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" + xmlns:foaf="http://xmlns.com/foaf/0.1/" + xmlns:gnome="http://api.gnome.org/doap-extensions#" + xmlns="http://usefulinc.com/ns/doap#"> + + <name xml:lang="en">at-spi2-atk</name> + <shortdesc xml:lang="en">Gtk module for bridging AT-SPI to Atk.</shortdesc> + <homepage rdf:resource="http://www.linuxfoundation.org/en/Accessibility/ATK/AT-SPI/AT-SPI_on_D-Bus"/> + <mailing-list rdf:resource="https://lists.linux-foundation.org/mailman/listinfo/accessibility-atspi"/> + + <maintainer> + <foaf:Person> + <foaf:name>Mark Doffman</foaf:name> + <foaf:mbox rdf:resource="mailto:mark.doffman@codethink.co.uk" /> + <gnome:userid>markdoffman</gnome:userid> + </foaf:Person> + </maintainer> + <maintainer> + <foaf:Person> + <foaf:name>Mike Gorse</foaf:name> + <foaf:mbox rdf:resource="mailto:mgorse@suse.com" /> + <gnome:userid>mgorse</gnome:userid> + </foaf:Person> + </maintainer> + <maintainer> + <foaf:Person> + <foaf:name>Li Yuan</foaf:name> + <foaf:mbox rdf:resource="mailto:lee.yuan@oracle.com" /> + <gnome:userid>liyuan</gnome:userid> + </foaf:Person> + </maintainer> +</Project> diff --git a/atk-adaptor/Makefile.am b/atk-adaptor/Makefile.am new file mode 100644 index 0000000..2bb3c6b --- /dev/null +++ b/atk-adaptor/Makefile.am @@ -0,0 +1,53 @@ +SUBDIRS= adaptors . gtk-2.0 + +lib_LTLIBRARIES = libatk-bridge-2.0.la + +libatk_bridge_2_0_la_CFLAGS = \ + $(DBUS_CFLAGS) \ + $(ATK_CFLAGS) \ + $(ATSPI_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/atk-adaptor/adaptors \ + $(P2P_CFLAGS) + +atkbridgeincludedir = $(pkgincludedir)/2.0/ +atkbridgeinclude_HEADERS = atk-bridge.h + +libatk_bridge_2_0_la_SOURCES = \ + accessible-leasing.c \ + accessible-leasing.h \ + accessible-cache.c \ + accessible-cache.h \ + accessible-register.c \ + accessible-register.h \ + accessible-stateset.c \ + accessible-stateset.h \ + bitarray.h \ + introspection.c \ + introspection.h \ + bridge.c \ + bridge.h \ + object.c \ + object.h \ + event.c \ + event.h \ + spi-dbus.c \ + spi-dbus.h \ + atk-bridge.h + +libatk_bridge_2_0_la_LIBADD = \ + $(DBUS_LIBS) \ + $(GMODULE_LIBS) \ + $(ATK_LIBS) \ + $(ATSPI_LIBS) \ + $(top_builddir)/droute/libdroute.la \ + $(top_builddir)/atk-adaptor/adaptors/libatk-bridge-adaptors.la + +libatk_bridge_2_0_la_LDFLAGS = \ + $(LT_VERSION_INFO) \ + -export-symbols $(srcdir)/atkbridge.symbols \ + -no-undefined \ + $(AM_LDFLAGS) + +EXTRA_DIST = Makefile.include \ + atkbridge.symbols diff --git a/atk-adaptor/Makefile.include b/atk-adaptor/Makefile.include new file mode 100644 index 0000000..8c47c7b --- /dev/null +++ b/atk-adaptor/Makefile.include @@ -0,0 +1,21 @@ +gtkmodule_LTLIBRARIES = libatk-bridge.la + +libatk_bridge_la_CFLAGS = \ + $(DBUS_CFLAGS) \ + $(ATK_CFLAGS) \ + $(ATSPI_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/atk-adaptor/adaptors \ + -I$(top_srcdir)/atk-adaptor/ + $(P2P_CFLAGS) + +libatk_bridge_la_LDFLAGS = -no-undefined \ + -module \ + -avoid-version \ + -rpath $(gtkmoduledir) + +libatk_bridge_la_LIBADD = $(DBUS_LIBS) \ + $(GMODULE_LIBS) \ + $(ATK_LIBS) \ + $(ATSPI_LIBS) \ + $(top_builddir)/atk-adaptor/libatk-bridge-2.0.la diff --git a/atk-adaptor/accessible-cache.c b/atk-adaptor/accessible-cache.c new file mode 100644 index 0000000..5065a00 --- /dev/null +++ b/atk-adaptor/accessible-cache.c @@ -0,0 +1,451 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <string.h> + +#include "accessible-cache.h" +#include "accessible-register.h" +#include "bridge.h" + +SpiCache *spi_global_cache = NULL; + +static gboolean +child_added_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data); + +static void +toplevel_added_listener (AtkObject * accessible, + guint index, AtkObject * child); + +static void +remove_object (GObject * source, GObject * gobj, gpointer data); + +static void +add_object (SpiCache * cache, GObject * gobj); + +static void +add_subtree (SpiCache *cache, AtkObject * accessible); + +static gboolean +add_pending_items (gpointer data); + +/*---------------------------------------------------------------------------*/ + +static void +spi_cache_finalize (GObject * object); + +/*---------------------------------------------------------------------------*/ + +enum +{ + OBJECT_ADDED, + OBJECT_REMOVED, + LAST_SIGNAL +}; +static guint cache_signals[LAST_SIGNAL] = { 0 }; + +/*---------------------------------------------------------------------------*/ + +G_DEFINE_TYPE (SpiCache, spi_cache, G_TYPE_OBJECT) + +static void spi_cache_class_init (SpiCacheClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + spi_cache_parent_class = g_type_class_ref (G_TYPE_OBJECT); + + object_class->finalize = spi_cache_finalize; + + cache_signals [OBJECT_ADDED] = \ + g_signal_new ("object-added", + SPI_CACHE_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + cache_signals [OBJECT_REMOVED] = \ + g_signal_new ("object-removed", + SPI_CACHE_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} + +static void +spi_cache_init (SpiCache * cache) +{ + cache->objects = g_hash_table_new (g_direct_hash, g_direct_equal); + cache->add_traversal = g_queue_new (); + +#ifdef SPI_ATK_DEBUG + if (g_thread_supported ()) + g_message ("AT-SPI: Threads enabled"); + + g_debug ("AT-SPI: Initial Atk tree regisration"); +#endif + + g_signal_connect (spi_global_register, + "object-deregistered", + (GCallback) remove_object, cache); + + add_subtree (cache, spi_global_app_data->root); + + cache->child_added_listener = atk_add_global_event_listener (child_added_listener, + "Gtk:AtkObject:children-changed"); + + g_signal_connect (G_OBJECT (spi_global_app_data->root), + "children-changed::add", + (GCallback) toplevel_added_listener, NULL); +} + +static void +spi_cache_finalize (GObject * object) +{ + SpiCache *cache = SPI_CACHE (object); + + while (!g_queue_is_empty (cache->add_traversal)) + g_object_unref (G_OBJECT (g_queue_pop_head (cache->add_traversal))); + g_queue_free (cache->add_traversal); + g_hash_table_unref (cache->objects); + + g_signal_handlers_disconnect_by_func (spi_global_register, + (GCallback) remove_object, cache); + + g_signal_handlers_disconnect_by_func (G_OBJECT (spi_global_app_data->root), + (GCallback) toplevel_added_listener, NULL); + + atk_remove_global_event_listener (cache->child_added_listener); + + G_OBJECT_CLASS (spi_cache_parent_class)->finalize (object); +} + +/*---------------------------------------------------------------------------*/ + +static void +remove_object (GObject * source, GObject * gobj, gpointer data) +{ + SpiCache *cache = SPI_CACHE (data); + + if (spi_cache_in (cache, gobj)) + { +#ifdef SPI_ATK_DEBUG + g_debug ("CACHE REM - %s - %d - %s\n", atk_object_get_name (ATK_OBJECT (gobj)), + atk_object_get_role (ATK_OBJECT (gobj)), + spi_register_object_to_path (spi_global_register, gobj)); +#endif + g_signal_emit (cache, cache_signals [OBJECT_REMOVED], 0, gobj); + g_hash_table_remove (cache->objects, gobj); + } + else if (g_queue_remove (cache->add_traversal, gobj)) + { + g_object_unref (gobj); + } +} + +static void +add_object (SpiCache * cache, GObject * gobj) +{ + g_return_if_fail (G_IS_OBJECT (gobj)); + + g_hash_table_insert (cache->objects, gobj, NULL); + +#ifdef SPI_ATK_DEBUG + g_debug ("CACHE ADD - %s - %d - %s\n", atk_object_get_name (ATK_OBJECT (gobj)), + atk_object_get_role (ATK_OBJECT (gobj)), + spi_register_object_to_path (spi_global_register, gobj)); +#endif + + g_signal_emit (cache, cache_signals [OBJECT_ADDED], 0, gobj); +} + +/*---------------------------------------------------------------------------*/ + +static GRecMutex cache_mutex; + +#ifdef SPI_ATK_DEBUG +static GStaticMutex recursion_check_guard = G_STATIC_MUTEX_INIT; +static gboolean recursion_check = FALSE; + +static gboolean +recursion_check_and_set () +{ + gboolean ret; + g_static_mutex_lock (&recursion_check_guard); + ret = recursion_check; + recursion_check = TRUE; + g_static_mutex_unlock (&recursion_check_guard); + return ret; +} + +static void +recursion_check_unset () +{ + g_static_mutex_lock (&recursion_check_guard); + recursion_check = FALSE; + g_static_mutex_unlock (&recursion_check_guard); +} +#endif /* SPI_ATK_DEBUG */ + +/*---------------------------------------------------------------------------*/ + +static void +append_children (AtkObject * accessible, GQueue * traversal) +{ + AtkObject *current; + guint i; + gint count = atk_object_get_n_accessible_children (accessible); + + if (count < 0) + count = 0; + for (i = 0; i < count; i++) + { + current = atk_object_ref_accessible_child (accessible, i); + if (current) + { + g_queue_push_tail (traversal, current); + } + } +} + +/* + * Adds a subtree of accessible objects + * to the cache at the accessible object provided. + * + * The leaf nodes do not have their children + * registered. A node is considered a leaf + * if it has the state "manages-descendants" + * or if it has already been registered. + */ +static void +add_subtree (SpiCache *cache, AtkObject * accessible) +{ + g_return_if_fail (ATK_IS_OBJECT (accessible)); + + g_object_ref (accessible); + g_queue_push_tail (cache->add_traversal, accessible); + add_pending_items (cache); +} + +static gboolean +add_pending_items (gpointer data) +{ + SpiCache *cache = SPI_CACHE (data); + AtkObject *current; + GQueue *to_add; + + to_add = g_queue_new (); + + while (!g_queue_is_empty (cache->add_traversal)) + { + AtkStateSet *set; + + /* cache->add_traversal holds a ref to current */ + current = g_queue_pop_head (cache->add_traversal); + set = atk_object_ref_state_set (current); + + if (set && !atk_state_set_contains_state (set, ATK_STATE_TRANSIENT)) + { + /* transfer the ref into to_add */ + g_queue_push_tail (to_add, current); + if (!spi_cache_in (cache, G_OBJECT (current)) && + !atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS) && + !atk_state_set_contains_state (set, ATK_STATE_DEFUNCT)) + { + append_children (current, cache->add_traversal); + } + } + else + { + /* drop the ref for the removed object */ + g_object_unref (current); + } + + if (set) + g_object_unref (set); + } + + while (!g_queue_is_empty (to_add)) + { + current = g_queue_pop_head (to_add); + + /* Make sure object is registerd so we are notified if it goes away */ + g_free (spi_register_object_to_path (spi_global_register, + G_OBJECT (current))); + + add_object (cache, G_OBJECT(current)); + g_object_unref (G_OBJECT (current)); + } + + g_queue_free (to_add); + cache->add_pending_idle = 0; + return FALSE; +} + +/*---------------------------------------------------------------------------*/ + +static gboolean +child_added_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + SpiCache *cache = spi_global_cache; + AtkObject *accessible; + + const gchar *detail = NULL; + + g_rec_mutex_lock (&cache_mutex); + + /* + * Ensure that only accessibles already in the cache + * have their signals processed. + */ + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + g_return_val_if_fail (ATK_IS_OBJECT (accessible), TRUE); + + if (spi_cache_in (cache, G_OBJECT(accessible))) + { +#ifdef SPI_ATK_DEBUG + if (recursion_check_and_set ()) + g_warning ("AT-SPI: Recursive use of cache module"); + + g_debug ("AT-SPI: Tree update listener"); +#endif + if (signal_hint->detail) + detail = g_quark_to_string (signal_hint->detail); + + if (detail && !strncmp (detail, "add", 3)) + { + gpointer child; + child = g_value_get_pointer (param_values + 2); + if (!child) + { + g_rec_mutex_unlock (&cache_mutex); + return TRUE; + } + + g_object_ref (child); + g_queue_push_tail (cache->add_traversal, child); + + if (cache->add_pending_idle == 0) + cache->add_pending_idle = g_idle_add (add_pending_items, cache); + } +#ifdef SPI_ATK_DEBUG + recursion_check_unset (); +#endif + } + + g_rec_mutex_unlock (&cache_mutex); + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +static void +toplevel_added_listener (AtkObject * accessible, + guint index, AtkObject * child) +{ + SpiCache *cache = spi_global_cache; + + g_rec_mutex_lock (&cache_mutex); + + g_return_if_fail (ATK_IS_OBJECT (accessible)); + + if (spi_cache_in (cache, G_OBJECT(accessible))) + { +#ifdef SPI_ATK_DEBUG + if (recursion_check_and_set ()) + g_warning ("AT-SPI: Recursive use of registration module"); + + g_debug ("AT-SPI: Toplevel added listener"); +#endif + if (!ATK_IS_OBJECT (child)) + { + child = atk_object_ref_accessible_child (accessible, index); + } + else + g_object_ref (child); + + g_queue_push_tail (cache->add_traversal, child); + + if (cache->add_pending_idle == 0) + cache->add_pending_idle = g_idle_add (add_pending_items, cache); +#ifdef SPI_ATK_DEBUG + recursion_check_unset (); +#endif + } + + g_rec_mutex_unlock (&cache_mutex); +} + +/*---------------------------------------------------------------------------*/ + +void +spi_cache_foreach (SpiCache * cache, GHFunc func, gpointer data) +{ + g_hash_table_foreach (cache->objects, func, data); +} + +gboolean +spi_cache_in (SpiCache * cache, GObject * object) +{ + if (!cache) + return FALSE; + + if (g_hash_table_lookup_extended (cache->objects, + object, + NULL, + NULL)) + return TRUE; + else + return FALSE; +} + +#ifdef SPI_ATK_DEBUG +void +spi_cache_print_info (GObject * obj) +{ + char * path = spi_register_object_to_path (spi_global_register, obj); + + if (spi_cache_in (spi_global_cache, obj)) + g_printf ("%s IC\n", path); + else + g_printf ("%s NC\n", path); + + if (path) + g_free (path); +} +#endif + +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/accessible-cache.h b/atk-adaptor/accessible-cache.h new file mode 100644 index 0000000..2f5be06 --- /dev/null +++ b/atk-adaptor/accessible-cache.h @@ -0,0 +1,67 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ACCESSIBLE_CACHE_H +#define ACCESSIBLE_CACHE_H + +#include <glib.h> +#include <glib-object.h> + +typedef struct _SpiCache SpiCache; +typedef struct _SpiCacheClass SpiCacheClass; + +G_BEGIN_DECLS + +#define SPI_CACHE_TYPE (spi_cache_get_type ()) +#define SPI_CACHE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SPI_CACHE_TYPE, SpiCache)) +#define SPI_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SPI_CACHE_TYPE, SpiCacheClass)) +#define SPI_IS_CACHE(o) (G_TYPE_CHECK__INSTANCE_TYPE ((o), SPI_CACHE_TYPE)) +#define SPI_IS_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SPI_CACHE_TYPE)) + +struct _SpiCache +{ + GObject parent; + + GHashTable * objects; + GQueue *add_traversal; + gint add_pending_idle; + + guint child_added_listener; +}; + +struct _SpiCacheClass +{ + GObjectClass parent_class; +}; + +GType spi_cache_get_type (void); + +extern SpiCache *spi_global_cache; + +void +spi_cache_foreach (SpiCache * cache, GHFunc func, gpointer data); + +gboolean +spi_cache_in (SpiCache * cache, GObject * object); + +G_END_DECLS +#endif /* ACCESSIBLE_CACHE_H */ diff --git a/atk-adaptor/accessible-leasing.c b/atk-adaptor/accessible-leasing.c new file mode 100644 index 0000000..dcddb0b --- /dev/null +++ b/atk-adaptor/accessible-leasing.c @@ -0,0 +1,214 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "accessible-leasing.h" + +#ifdef SPI_ATK_DEBUG +#include "accessible-cache.h" +#endif + +/*---------------------------------------------------------------------------*/ + +SpiLeasing *spi_global_leasing; + +typedef struct _ExpiryElement +{ + guint expiry_s; + GObject *object; +} ExpiryElement; + +static void spi_leasing_dispose (GObject * object); + +static void spi_leasing_finalize (GObject * object); + +static void add_expiry_timeout (SpiLeasing * leasing); + +/*---------------------------------------------------------------------------*/ + +G_DEFINE_TYPE (SpiLeasing, spi_leasing, G_TYPE_OBJECT) + +static void spi_leasing_class_init (SpiLeasingClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + spi_leasing_parent_class = g_type_class_ref (G_TYPE_OBJECT); + + object_class->finalize = spi_leasing_finalize; + object_class->dispose = spi_leasing_dispose; +} + +static void +spi_leasing_init (SpiLeasing * leasing) +{ + leasing->expiry_queue = g_queue_new (); + leasing->expiry_func_id = 0; +} + +static void +spi_leasing_finalize (GObject * object) +{ + SpiLeasing *leasing = SPI_LEASING (object); + + if (leasing->expiry_func_id) + g_source_remove (leasing->expiry_func_id); + g_queue_free (leasing->expiry_queue); + G_OBJECT_CLASS (spi_leasing_parent_class)->finalize (object); +} + +static void +spi_leasing_dispose (GObject * object) +{ + SpiLeasing *leasing = SPI_LEASING (object); + + ExpiryElement *head; + while ((head = g_queue_pop_head (leasing->expiry_queue))) + { + g_object_unref (head->object); + g_slice_free (ExpiryElement, head); + } + G_OBJECT_CLASS (spi_leasing_parent_class)->dispose (object); +} + +/*---------------------------------------------------------------------------*/ + +/* + End the lease on all objects whose expiry time has passed. + + Check when the next event is and set the next expiry func. +*/ +static gboolean +expiry_func (gpointer data) +{ + SpiLeasing *leasing = SPI_LEASING (data); + + ExpiryElement *head, *current; + GTimeVal t; + + g_get_current_time (&t); + + head = g_queue_peek_head (leasing->expiry_queue); + while (head != NULL && head->expiry_s <= t.tv_sec) + { + current = g_queue_pop_head (leasing->expiry_queue); + +#ifdef SPI_ATK_DEBUG + g_debug ("REVOKE - "); + spi_cache_print_info (current->object); +#endif + + g_object_unref (current->object); + g_slice_free (ExpiryElement, current); + + head = g_queue_peek_head (leasing->expiry_queue); + } + + leasing->expiry_func_id = 0; + add_expiry_timeout (leasing); + + return FALSE; +} + +/*---------------------------------------------------------------------------*/ + +/* + Checks if an expiry timeout is already scheduled, if so returns. + This is becasue events will always be added to the end of the queue. + Events are always later in time to previoulsy added events. + + Otherwise calculate the next wake time using the top of the queue + and add the next expiry function. + + This function is called when a lease is added or at the end of the + expiry function to add the next expiry timeout. +*/ +static void +add_expiry_timeout (SpiLeasing * leasing) +{ + ExpiryElement *elem; + GTimeVal t; + guint next_expiry; + + if (leasing->expiry_func_id != 0) + return; + + elem = (ExpiryElement *) g_queue_peek_head (leasing->expiry_queue); + if (elem == NULL) + return; + + /* The current time is implicitly rounded down here by ignoring the us */ + g_get_current_time (&t); + next_expiry = elem->expiry_s - t.tv_sec; + leasing->expiry_func_id = g_timeout_add_seconds (next_expiry, + expiry_func, leasing); +} + +/*---------------------------------------------------------------------------*/ + +/* + The lease time is expected to be in seconds, the rounding is going to be to + intervals of 1 second. + + The lease time is going to be rounded up, as the lease time should be + considered a MINIMUM that the object will be leased for. +*/ +#define LEASE_TIME_S 15 +#define EXPIRY_TIME_S (LEASE_TIME_S + 1) + +GObject * +spi_leasing_take (SpiLeasing * leasing, GObject * object) +{ + /* + Get the current time. + Quantize the time. + Add the release event to the queue. + Check the next expiry. + */ + + GTimeVal t; + guint expiry_s; + + ExpiryElement *elem; + + g_get_current_time (&t); + expiry_s = t.tv_sec + EXPIRY_TIME_S; + + elem = g_slice_new (ExpiryElement); + elem->expiry_s = expiry_s; + elem->object = g_object_ref (object); + + g_queue_push_tail (leasing->expiry_queue, elem); + + add_expiry_timeout (leasing); + +#ifdef SPI_ATK_DEBUG + g_debug ("LEASE - "); + spi_cache_print_info (object); +#endif + + return object; +} + +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/accessible-leasing.h b/atk-adaptor/accessible-leasing.h new file mode 100644 index 0000000..af01a78 --- /dev/null +++ b/atk-adaptor/accessible-leasing.h @@ -0,0 +1,61 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008, 2009 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ACCESSIBLE_LEASING_H +#define ACCESSIBLE_LEASING_H + +#include <glib.h> +#include <glib-object.h> + +typedef struct _SpiLeasing SpiLeasing; +typedef struct _SpiLeasingClass SpiLeasingClass; + +G_BEGIN_DECLS + +#define SPI_LEASING_TYPE (spi_leasing_get_type ()) +#define SPI_LEASING(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SPI_LEASING_TYPE, SpiLeasing)) +#define SPI_LEASING_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SPI_LEASING_TYPE, SpiLeasingClass)) +#define SPI_IS_LEASING(o) (G_TYPE_CHECK__INSTANCE_TYPE ((o), SPI_LEASING_TYPE)) +#define SPI_IS_LEASING_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SPI_LEASING_TYPE)) + +struct _SpiLeasing +{ + GObject parent; + + GQueue *expiry_queue; + guint expiry_func_id; +}; + +struct _SpiLeasingClass +{ + GObjectClass parent_class; +}; + +GType spi_leasing_get_type (void); + +extern SpiLeasing *spi_global_leasing; + +GObject *spi_leasing_take (SpiLeasing * leasing, GObject * object); + +G_END_DECLS +#endif /* ACCESSIBLE_LEASING_H */ diff --git a/atk-adaptor/accessible-register.c b/atk-adaptor/accessible-register.c new file mode 100644 index 0000000..7ca416f --- /dev/null +++ b/atk-adaptor/accessible-register.c @@ -0,0 +1,320 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008, 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "bridge.h" +#include "accessible-register.h" + +/* + * This module is responsible for keeping track of all the AtkObjects in + * the application, so that they can be accessed remotely and placed in + * a client side cache. + * + * To access an AtkObject remotely we need to provide a D-Bus object + * path for it. The D-Bus object paths used have a standard prefix + * (SPI_ATK_OBJECT_PATH_PREFIX). Appended to this prefix is a string + * representation of an integer reference. So to access an AtkObject + * remotely we keep a Hashtable that maps the given reference to + * the AtkObject pointer. An object in this hash table is said to be 'registered'. + * + * The architecture of AT-SPI dbus is such that AtkObjects are not + * remotely reference counted. This means that we need to keep track of + * object destruction. When an object is destroyed it must be 'deregistered' + * To do this lookup we keep a dbus-id attribute on each AtkObject. + * + */ + +#define SPI_ATK_PATH_PREFIX_LENGTH 27 +#define SPI_ATK_OBJECT_PATH_PREFIX "/org/a11y/atspi/accessible/" +#define SPI_ATK_OBJECT_PATH_ROOT "root" + +#define SPI_ATK_OBJECT_REFERENCE_TEMPLATE SPI_ATK_OBJECT_PATH_PREFIX "%d" + +#define SPI_DBUS_ID "spi-dbus-id" + +SpiRegister *spi_global_register = NULL; + +static const gchar * spi_register_root_path = SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT; + +enum +{ + OBJECT_REGISTERED, + OBJECT_DEREGISTERED, + LAST_SIGNAL +}; +static guint register_signals[LAST_SIGNAL] = { 0 }; + +/*---------------------------------------------------------------------------*/ + +static void +spi_register_finalize (GObject * object); + +/*---------------------------------------------------------------------------*/ + +G_DEFINE_TYPE (SpiRegister, spi_register, G_TYPE_OBJECT) + +static void spi_register_class_init (SpiRegisterClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + spi_register_parent_class = g_type_class_ref (G_TYPE_OBJECT); + + object_class->finalize = spi_register_finalize; + + register_signals [OBJECT_REGISTERED] = + g_signal_new ("object-registered", + SPI_REGISTER_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + register_signals [OBJECT_DEREGISTERED] = + g_signal_new ("object-deregistered", + SPI_REGISTER_TYPE, + G_SIGNAL_ACTION, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); +} + +static void +spi_register_init (SpiRegister * reg) +{ + reg->ref2ptr = g_hash_table_new (g_direct_hash, g_direct_equal); + reg->reference_counter = 0; +} + +static void +deregister_object (gpointer data, GObject * gobj) +{ + SpiRegister *reg = SPI_REGISTER (data); + + spi_register_deregister_object (reg, gobj, FALSE); +} + +static void +spi_register_remove_weak_ref (gpointer key, gpointer val, gpointer reg) +{ + g_object_weak_unref (val, deregister_object, reg); +} + +static void +spi_register_finalize (GObject * object) +{ + SpiRegister *reg = SPI_REGISTER (object); + + g_hash_table_foreach (reg->ref2ptr, spi_register_remove_weak_ref, reg); + g_hash_table_unref (reg->ref2ptr); + + G_OBJECT_CLASS (spi_register_parent_class)->finalize (object); +} + +/*---------------------------------------------------------------------------*/ + +/* + * Each AtkObject must be asssigned a D-Bus path (Reference) + * + * This function provides an integer reference for a new + * AtkObject. + * + * TODO: Make this reference a little more unique, this is shoddy. + */ +static guint +assign_reference (SpiRegister * reg) +{ + reg->reference_counter++; + /* Reference of 0 not allowed as used as direct key in hash table */ + if (reg->reference_counter == 0) + reg->reference_counter++; + return reg->reference_counter; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Returns the reference of the object, or 0 if it is not registered. + */ +static guint +object_to_ref (GObject * gobj) +{ + return GPOINTER_TO_INT (g_object_get_data (gobj, SPI_DBUS_ID)); +} + +/* + * Converts the Accessible object reference to its D-Bus object path + */ +static gchar * +ref_to_path (guint ref) +{ + return g_strdup_printf (SPI_ATK_OBJECT_REFERENCE_TEMPLATE, ref); +} + +/*---------------------------------------------------------------------------*/ + +/* + * Callback for when a registered AtkObject is destroyed. + * + * Removes the AtkObject from the reference lookup tables, meaning + * it is no longer exposed over D-Bus. + */ +void +spi_register_deregister_object (SpiRegister *reg, GObject *gobj, gboolean unref) +{ + guint ref; + + ref = object_to_ref (gobj); + if (ref != 0) + { + g_signal_emit (reg, + register_signals [OBJECT_DEREGISTERED], + 0, + gobj); + if (unref) + g_object_weak_unref (gobj, deregister_object, reg); + g_hash_table_remove (reg->ref2ptr, GINT_TO_POINTER (ref)); + +#ifdef SPI_ATK_DEBUG + g_debug ("DEREG - %d", ref); +#endif + } +} + +static void +register_object (SpiRegister * reg, GObject * gobj) +{ + guint ref; + g_return_if_fail (G_IS_OBJECT (gobj)); + + ref = assign_reference (reg); + + g_hash_table_insert (reg->ref2ptr, GINT_TO_POINTER (ref), gobj); + g_object_set_data (G_OBJECT (gobj), SPI_DBUS_ID, GINT_TO_POINTER (ref)); + g_object_weak_ref (G_OBJECT (gobj), deregister_object, reg); + +#ifdef SPI_ATK_DEBUG + g_debug ("REG - %d", ref); +#endif + + g_signal_emit (reg, register_signals [OBJECT_REGISTERED], 0, gobj); +} + +/*---------------------------------------------------------------------------*/ + +/* + * Used to lookup an GObject from its D-Bus path. + * + * If the D-Bus path is not found this function returns NULL. + */ +GObject * +spi_register_path_to_object (SpiRegister * reg, const char *path) +{ + guint index; + void *data; + + g_return_val_if_fail (path, NULL); + + if (strncmp (path, SPI_ATK_OBJECT_PATH_PREFIX, SPI_ATK_PATH_PREFIX_LENGTH) + != 0) + return NULL; + + path += SPI_ATK_PATH_PREFIX_LENGTH; /* Skip over the prefix */ + + /* Map the root path to the root object. */ + if (!g_strcmp0 (SPI_ATK_OBJECT_PATH_ROOT, path)) + return G_OBJECT (spi_global_app_data->root); + + index = atoi (path); + data = g_hash_table_lookup (reg->ref2ptr, GINT_TO_POINTER (index)); + if (data) + return G_OBJECT(data); + else + return NULL; +} + +GObject * +spi_global_register_path_to_object (const char * path) +{ + return spi_register_path_to_object (spi_global_register, path); +} + +/* + * Used to lookup a D-Bus path from the GObject. + * + * If the objects is not already registered, + * this function will register it. + */ +gchar * +spi_register_object_to_path (SpiRegister * reg, GObject * gobj) +{ + guint ref; + + if (gobj == NULL) + return NULL; + + /* Map the root object to the root path. */ + if ((void *)gobj == (void *)spi_global_app_data->root) + return g_strdup (spi_register_root_path); + + ref = object_to_ref (gobj); + if (!ref) + { + register_object (reg, gobj); + ref = object_to_ref (gobj); + } + + if (!ref) + return NULL; + else + return ref_to_path (ref); +} + +guint +spi_register_object_to_ref (GObject * gobj) +{ + return object_to_ref (gobj); +} + +/* + * Gets the path that indicates the accessible desktop object. + * This object is logically located on the registry daemon and not + * within any particular application. + */ +gchar * +spi_register_root_object_path () +{ + return g_strdup (SPI_ATK_OBJECT_PATH_PREFIX SPI_ATK_OBJECT_PATH_ROOT); +} + +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/accessible-register.h b/atk-adaptor/accessible-register.h new file mode 100644 index 0000000..5a62ebb --- /dev/null +++ b/atk-adaptor/accessible-register.h @@ -0,0 +1,80 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008, 2009 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ACCESSIBLE_REGISTER_H +#define ACCESSIBLE_REGISTER_H + +#include <glib.h> +#include <glib-object.h> + +typedef struct _SpiRegister SpiRegister; +typedef struct _SpiRegisterClass SpiRegisterClass; + +G_BEGIN_DECLS + +#define SPI_REGISTER_TYPE (spi_register_get_type ()) +#define SPI_REGISTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SPI_REGISTER_TYPE, SpiRegister)) +#define SPI_REGISTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SPI_REGISTER_TYPE, SpiRegisterClass)) +#define SPI_IS_REGISTER(o) (G_TYPE_CHECK__INSTANCE_TYPE ((o), SPI_REGISTER_TYPE)) +#define SPI_IS_REGISTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SPI_REGISTER_TYPE)) + +struct _SpiRegister +{ + GObject parent; + + GHashTable * ref2ptr; + guint reference_counter; +}; + +struct _SpiRegisterClass +{ + GObjectClass parent_class; +}; + +GType spi_register_get_type (void); + +extern SpiRegister *spi_global_register; + +/*---------------------------------------------------------------------------*/ + +GObject * +spi_register_path_to_object (SpiRegister * reg, const char *path); + +GObject * +spi_global_register_path_to_object (const char * path); + +gchar * +spi_register_object_to_path (SpiRegister * reg, GObject * gobj); + +guint +spi_register_object_to_ref (GObject * gobj); + +gchar * +spi_register_root_object_path (); + +void +spi_register_deregister_object (SpiRegister *reg, GObject *gobj, gboolean unref); + +/*---------------------------------------------------------------------------*/ + +#endif /* ACCESSIBLE_REGISTER_H */ diff --git a/atk-adaptor/accessible-stateset.c b/atk-adaptor/accessible-stateset.c new file mode 100644 index 0000000..3ab35ca --- /dev/null +++ b/atk-adaptor/accessible-stateset.c @@ -0,0 +1,208 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* stateset.c : implements the StateSet interface */ + +#include <config.h> +#include <stdio.h> +#include "accessible-stateset.h" +#include "bitarray.h" + + +static AtspiStateType *accessible_state_types = NULL; +static AtkStateType *atk_state_types = NULL; + + +static gboolean +spi_init_state_type_tables (void) +{ + gint i; + + if (accessible_state_types || atk_state_types) + return FALSE; + if (!accessible_state_types) + accessible_state_types = g_new (AtspiStateType, ATK_STATE_LAST_DEFINED); + if (!atk_state_types) + atk_state_types = g_new (AtkStateType, ATSPI_STATE_LAST_DEFINED); + g_return_val_if_fail (accessible_state_types, FALSE); + g_return_val_if_fail (atk_state_types, FALSE); + + for (i = 0; i < ATSPI_STATE_LAST_DEFINED; i++) + { + atk_state_types[i] = ATK_STATE_INVALID; + } + + for (i=0; i < ATK_STATE_LAST_DEFINED; i++) + { + accessible_state_types[i] = ATSPI_STATE_INVALID; + } + + accessible_state_types[ATK_STATE_ACTIVE] = ATSPI_STATE_ACTIVE; + atk_state_types[ATSPI_STATE_ACTIVE] = ATK_STATE_ACTIVE; + accessible_state_types[ATK_STATE_ARMED] = ATSPI_STATE_ARMED; + atk_state_types[ATSPI_STATE_ARMED] = ATK_STATE_ARMED; + accessible_state_types[ATK_STATE_BUSY] = ATSPI_STATE_BUSY; + atk_state_types[ATSPI_STATE_BUSY] = ATK_STATE_BUSY; + accessible_state_types[ATK_STATE_CHECKED] = ATSPI_STATE_CHECKED; + atk_state_types[ATSPI_STATE_CHECKED] = ATK_STATE_CHECKED; + accessible_state_types[ATK_STATE_DEFUNCT] = ATSPI_STATE_DEFUNCT; + atk_state_types[ATSPI_STATE_DEFUNCT] = ATK_STATE_DEFUNCT; + accessible_state_types[ATK_STATE_EDITABLE] = ATSPI_STATE_EDITABLE; + atk_state_types[ATSPI_STATE_EDITABLE] = ATK_STATE_EDITABLE; + accessible_state_types[ATK_STATE_ENABLED] = ATSPI_STATE_ENABLED; + atk_state_types[ATSPI_STATE_ENABLED] = ATK_STATE_ENABLED; + accessible_state_types[ATK_STATE_EXPANDABLE] = ATSPI_STATE_EXPANDABLE; + atk_state_types[ATSPI_STATE_EXPANDABLE] = ATK_STATE_EXPANDABLE; + accessible_state_types[ATK_STATE_EXPANDED] = ATSPI_STATE_EXPANDED; + atk_state_types[ATSPI_STATE_EXPANDED] = ATK_STATE_EXPANDED; + accessible_state_types[ATK_STATE_FOCUSABLE] = ATSPI_STATE_FOCUSABLE; + atk_state_types[ATSPI_STATE_FOCUSABLE] = ATK_STATE_FOCUSABLE; + accessible_state_types[ATK_STATE_FOCUSED] = ATSPI_STATE_FOCUSED; + atk_state_types[ATSPI_STATE_FOCUSED] = ATK_STATE_FOCUSED; + accessible_state_types[ATK_STATE_HORIZONTAL] = ATSPI_STATE_HORIZONTAL; + atk_state_types[ATSPI_STATE_HORIZONTAL] = ATK_STATE_HORIZONTAL; + accessible_state_types[ATK_STATE_ICONIFIED] = ATSPI_STATE_ICONIFIED; + atk_state_types[ATSPI_STATE_ICONIFIED] = ATK_STATE_ICONIFIED; + accessible_state_types[ATK_STATE_MODAL] = ATSPI_STATE_MODAL; + atk_state_types[ATSPI_STATE_MODAL] = ATK_STATE_MODAL; + accessible_state_types[ATK_STATE_MULTI_LINE] = ATSPI_STATE_MULTI_LINE; + atk_state_types[ATSPI_STATE_MULTI_LINE] = ATK_STATE_MULTI_LINE; + accessible_state_types[ATK_STATE_MULTISELECTABLE] = ATSPI_STATE_MULTISELECTABLE; + atk_state_types[ATSPI_STATE_MULTISELECTABLE] = ATK_STATE_MULTISELECTABLE; + accessible_state_types[ATK_STATE_OPAQUE] = ATSPI_STATE_OPAQUE; + atk_state_types[ATSPI_STATE_OPAQUE] = ATK_STATE_OPAQUE; + accessible_state_types[ATK_STATE_PRESSED] = ATSPI_STATE_PRESSED; + atk_state_types[ATSPI_STATE_PRESSED] = ATK_STATE_PRESSED; + accessible_state_types[ATK_STATE_RESIZABLE] = ATSPI_STATE_RESIZABLE; + atk_state_types[ATSPI_STATE_RESIZABLE] = ATK_STATE_RESIZABLE; + accessible_state_types[ATK_STATE_SELECTABLE] = ATSPI_STATE_SELECTABLE; + atk_state_types[ATSPI_STATE_SELECTABLE] = ATK_STATE_SELECTABLE; + accessible_state_types[ATK_STATE_SELECTED] = ATSPI_STATE_SELECTED; + atk_state_types[ATSPI_STATE_SELECTED] = ATK_STATE_SELECTED; + accessible_state_types[ATK_STATE_SENSITIVE] = ATSPI_STATE_SENSITIVE; + atk_state_types[ATSPI_STATE_SENSITIVE] = ATK_STATE_SENSITIVE; + accessible_state_types[ATK_STATE_SHOWING] = ATSPI_STATE_SHOWING; + atk_state_types[ATSPI_STATE_SHOWING] = ATK_STATE_SHOWING; + accessible_state_types[ATK_STATE_SINGLE_LINE] = ATSPI_STATE_SINGLE_LINE; + atk_state_types[ATSPI_STATE_SINGLE_LINE] = ATK_STATE_SINGLE_LINE; + accessible_state_types[ATK_STATE_STALE] = ATSPI_STATE_STALE; + atk_state_types[ATSPI_STATE_STALE] = ATK_STATE_STALE; + accessible_state_types[ATK_STATE_TRANSIENT] = ATSPI_STATE_TRANSIENT; + atk_state_types[ATSPI_STATE_TRANSIENT] = ATK_STATE_TRANSIENT; + accessible_state_types[ATK_STATE_VERTICAL] = ATSPI_STATE_VERTICAL; + atk_state_types[ATSPI_STATE_VERTICAL] = ATK_STATE_VERTICAL; + accessible_state_types[ATK_STATE_VISIBLE] = ATSPI_STATE_VISIBLE; + atk_state_types[ATSPI_STATE_VISIBLE] = ATK_STATE_VISIBLE; + accessible_state_types[ATK_STATE_MANAGES_DESCENDANTS] = ATSPI_STATE_MANAGES_DESCENDANTS; + atk_state_types[ATSPI_STATE_MANAGES_DESCENDANTS] = ATK_STATE_MANAGES_DESCENDANTS; + accessible_state_types[ATK_STATE_INDETERMINATE] = ATSPI_STATE_INDETERMINATE; + atk_state_types[ATSPI_STATE_INDETERMINATE] = ATK_STATE_INDETERMINATE; + accessible_state_types[ATK_STATE_TRUNCATED] = ATSPI_STATE_TRUNCATED; + atk_state_types[ATSPI_STATE_TRUNCATED] = ATK_STATE_TRUNCATED; + accessible_state_types[ATK_STATE_REQUIRED] = ATSPI_STATE_REQUIRED; + atk_state_types[ATSPI_STATE_REQUIRED] = ATK_STATE_REQUIRED; + accessible_state_types[ATK_STATE_INVALID_ENTRY] = ATSPI_STATE_INVALID_ENTRY; + atk_state_types[ATSPI_STATE_INVALID_ENTRY] = ATK_STATE_INVALID_ENTRY; + accessible_state_types[ATK_STATE_SUPPORTS_AUTOCOMPLETION] = ATSPI_STATE_SUPPORTS_AUTOCOMPLETION; + atk_state_types[ATSPI_STATE_SUPPORTS_AUTOCOMPLETION] = ATK_STATE_SUPPORTS_AUTOCOMPLETION; + accessible_state_types[ATK_STATE_SELECTABLE_TEXT] = ATSPI_STATE_SELECTABLE_TEXT; + atk_state_types[ATSPI_STATE_SELECTABLE_TEXT] = ATK_STATE_SELECTABLE_TEXT; + accessible_state_types[ATK_STATE_DEFAULT] = ATSPI_STATE_IS_DEFAULT; + atk_state_types[ATSPI_STATE_IS_DEFAULT] = ATK_STATE_DEFAULT; + accessible_state_types[ATK_STATE_VISITED] = ATSPI_STATE_VISITED; + atk_state_types[ATSPI_STATE_VISITED] = ATK_STATE_VISITED; + accessible_state_types[ATK_STATE_HAS_POPUP] = ATSPI_STATE_HAS_POPUP; + atk_state_types[ATSPI_STATE_HAS_POPUP] = ATK_STATE_HAS_POPUP; + accessible_state_types[ATK_STATE_CHECKABLE] = ATSPI_STATE_CHECKABLE; + atk_state_types[ATSPI_STATE_CHECKABLE] = ATK_STATE_CHECKABLE; + + return TRUE; +} + +static inline AtkState +state_spi_to_atk (AtspiStateType state) +{ + guint idx = state; + if (idx < ATSPI_STATE_LAST_DEFINED) + return atk_state_types [idx]; + else + return ATK_STATE_INVALID; +} + +AtkState +spi_atk_state_from_spi_state (AtspiStateType state) +{ + spi_init_state_type_tables (); + return state_spi_to_atk (state); +} + +AtkStateSet * +spi_state_set_cache_from_sequence (GArray *seq) +{ + int i; + AtkStateSet *set; + AtkStateType *states; + + spi_init_state_type_tables (); + + states = g_newa (AtkStateType, seq->len); + for (i = 0; i < seq->len; i++) + states [i] = state_spi_to_atk (g_array_index (seq, dbus_int32_t, i)); + + set = atk_state_set_new (); + atk_state_set_add_states (set, states, seq->len); + + g_array_free (seq, TRUE); + return set; +} + +void +spi_atk_state_to_dbus_array (AtkObject * object, dbus_uint32_t * array) +{ + AtkStateSet *set = atk_object_ref_state_set (object); + spi_atk_state_set_to_dbus_array (set, array); + g_object_unref (set); +} + +void +spi_atk_state_set_to_dbus_array (AtkStateSet * set, dbus_uint32_t * array) +{ + int i; + + array[0] = 0; + array[1] = 0; + if (!set) + return; + spi_init_state_type_tables (); + + g_assert (ATK_STATE_LAST_DEFINED <= 64); + for (i = 0; i < ATK_STATE_LAST_DEFINED; i++) + { + if (atk_state_set_contains_state (set, i)) + { + int a = accessible_state_types[i]; + g_assert (a < 64); + BITARRAY_SET (array, a); + } + } +} diff --git a/atk-adaptor/accessible-stateset.h b/atk-adaptor/accessible-stateset.h new file mode 100644 index 0000000..e27e3c5 --- /dev/null +++ b/atk-adaptor/accessible-stateset.h @@ -0,0 +1,51 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ACCESSSIBLE_STATE_SET_H_ +#define ACCESSIBLE_STATE_SET_H_ + +#include <atk/atkstateset.h> +#include "atspi/atspi.h" + +G_BEGIN_DECLS + +/* private - internal API to abstract away atk API */ +AtkStateSet *spi_state_set_cache_from_sequence(GArray *seq); +AtkState spi_atk_state_from_spi_state (AtspiStateType state); +void spi_atk_state_to_dbus_array (AtkObject * object, dbus_uint32_t * array); +void spi_atk_state_set_to_dbus_array (AtkStateSet *set, dbus_uint32_t * array); +#define spi_state_set_cache_ref(s) g_object_ref (s) +#define spi_state_set_cache_unref(s) g_object_unref (s) +#define spi_state_set_cache_new(seq) spi_state_set_cache_from_sequence (seq) +#define spi_state_set_cache_contains(s,a) atk_state_set_contains_state (ATK_STATE_SET (s), \ + spi_atk_state_from_spi_state (a)) +#define spi_state_set_cache_add(s,a) atk_state_set_add_state (ATK_STATE_SET (s), \ + spi_atk_state_from_spi_state (a)) +#define spi_state_set_cache_remove(s,a) atk_state_set_remove_state (ATK_STATE_SET (s), \ + spi_atk_state_from_spi_state (a)) +#define spi_state_set_cache_xor(a,b) atk_state_set_xor_sets (ATK_STATE_SET (a), ATK_STATE_SET (b)) +#define spi_state_set_cache_is_empty(a) atk_state_set_is_empty (ATK_STATE_SET (a)) + +G_END_DECLS + +#endif /* ACCESSIBLE_STATE_SET_H_ */ diff --git a/atk-adaptor/adaptors/Makefile.am b/atk-adaptor/adaptors/Makefile.am new file mode 100644 index 0000000..7624b57 --- /dev/null +++ b/atk-adaptor/adaptors/Makefile.am @@ -0,0 +1,34 @@ +noinst_LTLIBRARIES = libatk-bridge-adaptors.la + +libatk_bridge_adaptors_la_CFLAGS =\ + $(DBUS_CFLAGS) \ + $(ATK_CFLAGS) \ + $(ATSPI_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/atk-adaptor \ + $(P2P_CFLAGS) + +libatk_bridge_adaptors_la_LIBADD =\ + $(DBUS_LIBS) \ + $(ATK_LIBS) \ + $(ATSPI_LIBS) + +libatk_bridge_adaptors_la_SOURCES =\ + accessible-adaptor.c \ + action-adaptor.c \ + adaptors.h \ + application-adaptor.c \ + cache-adaptor.c \ + collection-adaptor.c \ + component-adaptor.c \ + document-adaptor.c \ + editabletext-adaptor.c \ + hyperlink-adaptor.c \ + hypertext-adaptor.c \ + image-adaptor.c \ + selection-adaptor.c \ + socket-adaptor.c \ + table-adaptor.c \ + table-cell-adaptor.c \ + text-adaptor.c \ + value-adaptor.c diff --git a/atk-adaptor/adaptors/accessible-adaptor.c b/atk-adaptor/adaptors/accessible-adaptor.c new file mode 100644 index 0000000..058b116 --- /dev/null +++ b/atk-adaptor/adaptors/accessible-adaptor.c @@ -0,0 +1,558 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "atspi/atspi.h" +#include "spi-dbus.h" +#include "accessible-stateset.h" +#include "object.h" +#include "introspection.h" +#include <string.h> + +static dbus_bool_t +impl_get_Name (DBusMessageIter * iter, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE); + + return droute_return_v_string (iter, atk_object_get_name (object)); +} + +static dbus_bool_t +impl_get_Description (DBusMessageIter * iter, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE); + + return droute_return_v_string (iter, atk_object_get_description (object)); +} + +static dbus_bool_t +impl_get_Locale (DBusMessageIter * iter, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE); + + return droute_return_v_string (iter, atk_object_get_object_locale (object)); +} + +static dbus_bool_t +impl_get_Parent (DBusMessageIter * iter, void *user_data) +{ + AtkObject *obj = (AtkObject *) user_data; + AtkObject *parent; + DBusMessageIter iter_variant; + dbus_uint32_t role; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE); + + role = spi_accessible_role_from_atk_role (atk_object_get_role (obj)); + + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(so)", + &iter_variant); + + parent = atk_object_get_parent (obj); + if (parent == NULL) + { + /* TODO, move in to a 'Plug' wrapper. */ + if (ATK_IS_PLUG (obj)) + { + char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent"); + char *bus_parent; + char *path_parent; + + if (id) + { + bus_parent = g_strdup (id); + if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':'))) + { + DBusMessageIter iter_parent; + *(path_parent++) = '\0'; + dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL, + &iter_parent); + dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_STRING, &bus_parent); + dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_OBJECT_PATH, &path_parent); + dbus_message_iter_close_container (&iter_variant, &iter_parent); + } + else + { + spi_object_append_null_reference (&iter_variant); + } + } + else + { + spi_object_append_null_reference (&iter_variant); + } + } + else if (role != ATSPI_ROLE_APPLICATION) + spi_object_append_null_reference (&iter_variant); + else + spi_object_append_desktop_reference (&iter_variant); + } + else + { + spi_object_append_reference (&iter_variant, parent); + } + + + dbus_message_iter_close_container (iter, &iter_variant); + return TRUE; +} + +static dbus_bool_t +impl_get_ChildCount (DBusMessageIter * iter, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + int childCount; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE); + + childCount = (ATK_IS_SOCKET (object) && atk_socket_is_occupied (ATK_SOCKET (object))) + ? 1 + : atk_object_get_n_accessible_children (object); + return droute_return_v_int32 (iter, childCount); +} + +static DBusMessage * +impl_GetChildAtIndex (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + DBusMessage *reply; + dbus_int32_t i; + AtkObject *child; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + if (ATK_IS_SOCKET (object) && atk_socket_is_occupied (ATK_SOCKET (object)) && i == 0) + { + AtkSocket *socket = ATK_SOCKET (object); + gchar *child_name, *child_path; + child_name = g_strdup (socket->embedded_plug_id); + child_path = g_utf8_strchr (child_name + 1, -1, ':'); + if (child_path) + { + DBusMessageIter iter, iter_socket; + *(child_path++) = '\0'; + reply = dbus_message_new_method_return (message); + if (!reply) + return NULL; + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_STRUCT, NULL, + &iter_socket); + dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_STRING, &child_name); + dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_OBJECT_PATH, &child_path); + dbus_message_iter_close_container (&iter, &iter_socket); + return reply; + } + g_free (child_name); + } + child = atk_object_ref_accessible_child (object, i); + reply = spi_object_return_reference (message, child); + g_object_unref (child); + + return reply; +} + +static DBusMessage * +impl_GetChildren (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + gint i; + gint count; + DBusMessage *reply; + DBusMessageIter iter, iter_array; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + count = atk_object_get_n_accessible_children (object); + reply = dbus_message_new_method_return (message); + if (!reply) + goto oom; + dbus_message_iter_init_append (reply, &iter); + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array)) + goto oom; + for (i = 0; i < count; i++) + { + AtkObject *child = atk_object_ref_accessible_child (object, i); + spi_object_append_reference (&iter_array, child); + if (child) + g_object_unref (child); + } + if (!dbus_message_iter_close_container (&iter, &iter_array)) + goto oom; + return reply; +oom: + // TODO: handle out-of-memory + return reply; +} + +static DBusMessage * +impl_GetIndexInParent (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + dbus_int32_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + + rv = atk_object_get_index_in_parent (object); + reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv, DBUS_TYPE_INVALID); + return reply; +} + +static gboolean +spi_init_relation_type_table (AtspiRelationType * types) +{ + gint i; + + for (i = 0; i < ATK_RELATION_LAST_DEFINED; i++) + types[i] = ATSPI_RELATION_NULL; + + types[ATK_RELATION_CONTROLLED_BY] = ATSPI_RELATION_CONTROLLED_BY; + types[ATK_RELATION_CONTROLLER_FOR] = ATSPI_RELATION_CONTROLLER_FOR; + types[ATK_RELATION_LABEL_FOR] = ATSPI_RELATION_LABEL_FOR; + types[ATK_RELATION_LABELLED_BY] = ATSPI_RELATION_LABELLED_BY; + types[ATK_RELATION_MEMBER_OF] = ATSPI_RELATION_MEMBER_OF; + types[ATK_RELATION_NODE_CHILD_OF] = ATSPI_RELATION_NODE_CHILD_OF; + types[ATK_RELATION_FLOWS_TO] = ATSPI_RELATION_FLOWS_TO; + types[ATK_RELATION_FLOWS_FROM] = ATSPI_RELATION_FLOWS_FROM; + types[ATK_RELATION_SUBWINDOW_OF] = ATSPI_RELATION_SUBWINDOW_OF; + types[ATK_RELATION_EMBEDS] = ATSPI_RELATION_EMBEDS; + types[ATK_RELATION_EMBEDDED_BY] = ATSPI_RELATION_EMBEDDED_BY; + types[ATK_RELATION_POPUP_FOR] = ATSPI_RELATION_POPUP_FOR; + types[ATK_RELATION_PARENT_WINDOW_OF] = + ATSPI_RELATION_PARENT_WINDOW_OF; + types[ATK_RELATION_DESCRIPTION_FOR] = + ATSPI_RELATION_DESCRIPTION_FOR; + types[ATK_RELATION_DESCRIBED_BY] = ATSPI_RELATION_DESCRIBED_BY; + types[ATK_RELATION_NODE_PARENT_OF] = ATSPI_RELATION_NODE_PARENT_OF; + + return TRUE; +} + +static AtspiRelationType +spi_relation_type_from_atk_relation_type (AtkRelationType type) +{ + static gboolean is_initialized = FALSE; + static AtspiRelationType + spi_relation_type_table[ATK_RELATION_LAST_DEFINED]; + AtspiRelationType spi_type; + + if (!is_initialized) + is_initialized = spi_init_relation_type_table (spi_relation_type_table); + + if (type > ATK_RELATION_NULL && type < ATK_RELATION_LAST_DEFINED) + spi_type = spi_relation_type_table[type]; + else + spi_type = ATSPI_RELATION_EXTENDED; + return spi_type; +} + +static DBusMessage * +impl_GetRelationSet (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + DBusMessage *reply; + AtkRelationSet *set; + DBusMessageIter iter, iter_array, iter_struct, iter_targets; + gint count; + gint i, j; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + reply = dbus_message_new_method_return (message); + if (!reply) + return NULL; + set = atk_object_ref_relation_set (object); + dbus_message_iter_init_append (reply, &iter); + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "(ua(so))", &iter_array)) + { + goto oom; + } + count = 0; + if (set) + count = atk_relation_set_get_n_relations (set); + for (i = 0; i < count; i++) + { + AtkRelation *r = atk_relation_set_get_relation (set, i); + AtkRelationType rt; + GPtrArray *target; + dbus_uint32_t type; + if (!r) + continue; + rt = atk_relation_get_relation_type (r); + type = spi_relation_type_from_atk_relation_type (rt); + target = atk_relation_get_target (r); + if (!dbus_message_iter_open_container + (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct)) + { + goto oom; + } + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &type); + if (!dbus_message_iter_open_container + (&iter_struct, DBUS_TYPE_ARRAY, "(so)", &iter_targets)) + { + goto oom; + } + for (j = 0; j < target->len; j++) + { + AtkObject *obj = target->pdata[j]; + if (!obj) + continue; + spi_object_append_reference (&iter_targets, obj); + } + dbus_message_iter_close_container (&iter_struct, &iter_targets); + dbus_message_iter_close_container (&iter_array, &iter_struct); + } + dbus_message_iter_close_container (&iter, &iter_array); +oom: + if (set) + g_object_unref (set); + // TODO: handle out of memory */ + return reply; +} + +static DBusMessage * +impl_GetRole (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + gint role; + dbus_uint32_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + role = atk_object_get_role (object); + rv = spi_accessible_role_from_atk_role (role); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetRoleName (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + gint role; + const char *role_name; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + role = atk_object_get_role (object); + role_name = atk_role_get_name (role); + if (!role_name) + role_name = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetLocalizedRoleName (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + gint role; + const char *role_name; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + role = atk_object_get_role (object); + role_name = atk_role_get_localized_name (role); + if (!role_name) + role_name = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetState (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + + DBusMessage *reply = NULL; + DBusMessageIter iter, iter_array; + + dbus_uint32_t states[2]; + + guint count; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + + reply = dbus_message_new_method_return (message); + dbus_message_iter_init_append (reply, &iter); + + spi_atk_state_to_dbus_array (object, states); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "u", &iter_array); + for (count = 0; count < 2; count++) + { + dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_UINT32, + &states[count]); + } + dbus_message_iter_close_container (&iter, &iter_array); + return reply; +} + +static DBusMessage * +impl_GetAttributes (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + AtkAttributeSet *attributes; + DBusMessage *reply = NULL; + DBusMessageIter iter; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + + attributes = atk_object_get_attributes (object); + + reply = dbus_message_new_method_return (message); + dbus_message_iter_init_append (reply, &iter); + spi_object_append_attribute_set (&iter, attributes); + + atk_attribute_set_free (attributes); + + return reply; +} + +static dbus_bool_t +impl_get_Attributes (DBusMessageIter * iter, void *user_data) +{ + DBusMessageIter iter_variant; + AtkObject *object = (AtkObject *) user_data; + AtkAttributeSet *attributes; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE); + + attributes = atk_object_get_attributes (object); + + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "a{ss}", &iter_variant); + spi_object_append_attribute_set (&iter_variant, attributes); + dbus_message_iter_close_container (iter, &iter_variant); + + atk_attribute_set_free (attributes); + + return TRUE; +} + +static DBusMessage * +impl_GetApplication (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + return spi_object_return_reference (message, atk_get_root ()); +} + +static DBusMessage * +impl_GetInterfaces (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + DBusMessage *reply; + DBusMessageIter iter, iter_array; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", + &iter_array); + spi_object_append_interfaces (&iter_array, object); + dbus_message_iter_close_container (&iter, &iter_array); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetChildAtIndex, "GetChildAtIndex"}, + {impl_GetChildren, "GetChildren"}, + {impl_GetIndexInParent, "GetIndexInParent"}, + {impl_GetRelationSet, "GetRelationSet"}, + {impl_GetRole, "GetRole"}, + {impl_GetRoleName, "GetRoleName"}, + {impl_GetLocalizedRoleName, "GetLocalizedRoleName"}, + {impl_GetState, "GetState"}, + {impl_GetAttributes, "GetAttributes"}, + {impl_GetApplication, "GetApplication"}, + {impl_GetInterfaces, "GetInterfaces"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_Name, NULL, "Name"}, + {impl_get_Description, NULL, "Description"}, + {impl_get_Locale, NULL, "Locale"}, + {impl_get_Parent, NULL, "Parent"}, + {impl_get_ChildCount, NULL, "ChildCount"}, + {impl_get_Attributes, NULL, "Attributes"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_accessible (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_ACCESSIBLE, + spi_org_a11y_atspi_Accessible, + methods, properties); +}; diff --git a/atk-adaptor/adaptors/action-adaptor.c b/atk-adaptor/adaptors/action-adaptor.c new file mode 100644 index 0000000..a6c409d --- /dev/null +++ b/atk-adaptor/adaptors/action-adaptor.c @@ -0,0 +1,257 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" + +#include "introspection.h" + +static dbus_bool_t +impl_get_NActions (DBusMessageIter * iter, void *user_data) +{ + AtkAction *action = (AtkAction *) user_data; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), FALSE); + return droute_return_v_int32 (iter, atk_action_get_n_actions (action)); +} + +static DBusMessage * +impl_get_description (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkAction *action = (AtkAction *) user_data; + DBusMessage *reply; + dbus_int32_t index; + const char *desc; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + desc = atk_action_get_description (action, index); + if (!desc) + desc = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &desc, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_get_name (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + DBusMessage *reply; + dbus_int32_t index; + const char *name; + AtkAction *action = (AtkAction *) user_data; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + name = atk_action_get_name (action, index); + if (!name) + name = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_get_localized_name (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + DBusMessage *reply; + dbus_int32_t index; + const char *name; + AtkAction *action = (AtkAction *) user_data; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + name = atk_action_get_localized_name (action, index); + if (!name) + name = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_get_keybinding (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + DBusMessage *reply; + dbus_int32_t index; + const char *kb; + AtkAction *action = (AtkAction *) user_data; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + kb = atk_action_get_keybinding (action, index); + if (!kb) + kb = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &kb, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetActions (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkAction *action = (AtkAction *) user_data; + DBusMessage *reply; + gint count; + gint i; + DBusMessageIter iter, iter_array, iter_struct; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), + droute_not_yet_handled_error (message)); + count = atk_action_get_n_actions (action); + reply = dbus_message_new_method_return (message); + if (!reply) + goto oom; + dbus_message_iter_init_append (reply, &iter); + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "(sss)", &iter_array)) + goto oom; + for (i = 0; i < count; i++) + { + const char *name = atk_action_get_name (action, i); + const char *lname = atk_action_get_localized_name (action, i); + const char *desc = atk_action_get_description (action, i); + const char *kb = atk_action_get_keybinding (action, i); + if (!name) + name = ""; + if (!lname) + lname = ""; + if (!desc) + desc = ""; + if (!kb) + kb = ""; + if (!dbus_message_iter_open_container + (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct)) + goto oom; + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &lname); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &kb); + if (!dbus_message_iter_close_container (&iter_array, &iter_struct)) + goto oom; + } + if (!dbus_message_iter_close_container (&iter, &iter_array)) + goto oom; + return reply; +oom: + // TODO: handle out-of-memory + return reply; +} + +static DBusMessage * +impl_DoAction (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkAction *action = (AtkAction *) user_data; + dbus_int32_t index; + dbus_bool_t rv = TRUE; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_ACTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + dbus_connection_send (bus, reply, NULL); + dbus_message_unref (reply); + atk_action_do_action (action, index); + return NULL; +} + +DRouteMethod methods[] = { + {impl_get_description, "GetDescription"} + , + {impl_get_name, "GetName"} + , + {impl_get_localized_name, "GetLocalizedName"} + , + {impl_get_keybinding, "GetKeyBinding"} + , + {impl_GetActions, "GetActions"} + , + {impl_DoAction, "DoAction"} + , + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_NActions, NULL, "NActions"}, + {NULL, NULL} +}; + +void +spi_initialize_action (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_ACTION, + spi_org_a11y_atspi_Action, methods, properties); +}; diff --git a/atk-adaptor/adaptors/adaptors.h b/atk-adaptor/adaptors/adaptors.h new file mode 100644 index 0000000..395114b --- /dev/null +++ b/atk-adaptor/adaptors/adaptors.h @@ -0,0 +1,50 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ADAPTORS_H +#define ADAPTORS_H + +#include <atk/atk.h> +#include <droute/droute.h> + +AtspiRole spi_accessible_role_from_atk_role (AtkRole role); + +void spi_initialize_accessible (DRoutePath * path); +void spi_initialize_action (DRoutePath * path); +void spi_initialize_application (DRoutePath * path); +void spi_initialize_collection (DRoutePath * path); +void spi_initialize_component (DRoutePath * path); +void spi_initialize_document (DRoutePath * path); +void spi_initialize_editabletext (DRoutePath * path); +void spi_initialize_hyperlink (DRoutePath * path); +void spi_initialize_hypertext (DRoutePath * path); +void spi_initialize_image (DRoutePath * path); +void spi_initialize_selection (DRoutePath * path); +void spi_initialize_socket (DRoutePath * path); +void spi_initialize_table (DRoutePath * path); +void spi_initialize_table_cell (DRoutePath * path); +void spi_initialize_text (DRoutePath * path); +void spi_initialize_value (DRoutePath * path); +void spi_initialize_cache (DRoutePath * path); + +#endif /* ADAPTORS_H */ diff --git a/atk-adaptor/adaptors/application-adaptor.c b/atk-adaptor/adaptors/application-adaptor.c new file mode 100644 index 0000000..b74e5be --- /dev/null +++ b/atk-adaptor/adaptors/application-adaptor.c @@ -0,0 +1,156 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> + +#include "spi-dbus.h" +#include "introspection.h" + +/* for spi_global_app_data is there a better way? */ +#include "../bridge.h" + +static dbus_bool_t +impl_get_ToolkitName (DBusMessageIter * iter, void *user_data) +{ + return droute_return_v_string (iter, atk_get_toolkit_name ()); +} + +static dbus_bool_t +impl_get_Version (DBusMessageIter * iter, void *user_data) +{ + return droute_return_v_string (iter, atk_get_toolkit_version ()); +} + +static dbus_bool_t +impl_get_AtspiVersion (DBusMessageIter * iter, void *user_data) +{ + return droute_return_v_string (iter, "2.0"); +} + +static dbus_int32_t id; + +static dbus_bool_t +impl_get_Id (DBusMessageIter * iter, void *user_data) +{ + return droute_return_v_int32 (iter, id); +} + +static dbus_bool_t +impl_set_Id (DBusMessageIter * iter, void *user_data) +{ + id = droute_get_v_int32 (iter); + return TRUE; +} + +static DBusMessage * +impl_registerToolkitEventListener (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + return NULL; +} + +static DBusMessage * +impl_registerObjectEventListener (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + return NULL; +} + +static DBusMessage * +impl_pause (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + return NULL; +} + +static DBusMessage * +impl_resume (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + return NULL; +} + +static DBusMessage * +impl_GetLocale (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + return NULL; +} + +static DBusMessage * +impl_get_app_bus(DBusConnection *bus, DBusMessage *msg, void *data) +{ +DBusMessage *reply; + + if (bus == spi_global_app_data->bus) + spi_atk_add_client (dbus_message_get_sender (msg)); + + if (!spi_global_app_data->app_bus_addr) + spi_atk_create_socket (spi_global_app_data); + +reply = dbus_message_new_method_return(msg); +if (reply) + { + const char *retval = (atspi_is_initialized () ? + "": + spi_global_app_data->app_bus_addr); + if (!retval) + retval = ""; + dbus_message_append_args(reply, DBUS_TYPE_STRING, &retval, DBUS_TYPE_INVALID); + } + +return reply; +} + +static DRouteMethod methods[] = { + {impl_registerToolkitEventListener, "registerToolkitEventListener"}, + {impl_registerObjectEventListener, "registerObjectEventListener"}, + {impl_pause, "pause"}, + {impl_resume, "resume"}, + {impl_GetLocale, "GetLocale"}, + {impl_get_app_bus, "GetApplicationBusAddress"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_ToolkitName, NULL, "ToolkitName"}, + {impl_get_Version, NULL, "Version"}, + {impl_get_AtspiVersion, NULL, "AtspiVersion"}, + {impl_get_Id, impl_set_Id, "Id"}, + {NULL, NULL, NULL} +}; + +/*static long +obj_is_root (const char *path, void *user_data) +{ + AtkObject *obj = atk_dbus_get_object (path); + return (obj == atk_get_root ()); +}*/ + +void +spi_initialize_application (DRoutePath * path) +{ + droute_path_add_interface (path, + ATSPI_DBUS_INTERFACE_APPLICATION, + spi_org_a11y_atspi_Application, + methods, properties); +}; diff --git a/atk-adaptor/adaptors/cache-adaptor.c b/atk-adaptor/adaptors/cache-adaptor.c new file mode 100644 index 0000000..cad7396 --- /dev/null +++ b/atk-adaptor/adaptors/cache-adaptor.c @@ -0,0 +1,345 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * Copyright 2008, 2009 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include <atk/atk.h> +#include <droute/droute.h> + +#include "spi-dbus.h" +#include "accessible-stateset.h" +#include "accessible-cache.h" +#include "bridge.h" +#include "object.h" +#include "introspection.h" + +/* TODO - This should possibly be a common define */ +#define SPI_OBJECT_PREFIX "/org/a11y/atspi" +#define SPI_CACHE_OBJECT_SUFFIX "/cache" +#define SPI_CACHE_OBJECT_PATH SPI_OBJECT_PREFIX SPI_CACHE_OBJECT_SUFFIX + +#define SPI_OBJECT_REFERENCE_SIGNATURE "(" \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_OBJECT_PATH_AS_STRING \ + ")" + +#define SPI_CACHE_ITEM_SIGNATURE "(" \ + SPI_OBJECT_REFERENCE_SIGNATURE \ + SPI_OBJECT_REFERENCE_SIGNATURE \ + SPI_OBJECT_REFERENCE_SIGNATURE \ + DBUS_TYPE_ARRAY_AS_STRING \ + SPI_OBJECT_REFERENCE_SIGNATURE \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_UINT32_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_TYPE_UINT32_AS_STRING \ + ")" + +/*---------------------------------------------------------------------------*/ + +/* + * Marshals the given AtkObject into the provided D-Bus iterator. + * + * The object is marshalled including all its client side cache data. + * The format of the structure is (o(so)a(so)assusau). + */ +static void +append_cache_item (AtkObject * obj, gpointer data) +{ + DBusMessageIter iter_struct, iter_sub_array; + dbus_uint32_t states[2]; + int count; + AtkStateSet *set; + DBusMessageIter *iter_array = (DBusMessageIter *) data; + + const char *name, *desc; + dbus_uint32_t role; + + set = atk_object_ref_state_set (obj); + { + AtkObject *application, *parent; + + dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, + &iter_struct); + + /* Marshall object path */ + spi_object_append_reference (&iter_struct, obj); + + role = spi_accessible_role_from_atk_role (atk_object_get_role (obj)); + + /* Marshall application */ + application = spi_global_app_data->root; + spi_object_append_reference (&iter_struct, application); + + /* Marshall parent */ + parent = atk_object_get_parent (obj); + if (parent == NULL) + { + /* TODO, move in to a 'Plug' wrapper. */ + if (ATK_IS_PLUG (obj)) + { + char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent"); + char *bus_parent; + char *path_parent; + + if (id) + { + bus_parent = g_strdup (id); + if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':'))) + { + DBusMessageIter iter_parent; + *(path_parent++) = '\0'; + dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_STRUCT, NULL, + &iter_parent); + dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_STRING, &bus_parent); + dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_OBJECT_PATH, &path_parent); + dbus_message_iter_close_container (&iter_struct, &iter_parent); + } + else + { + spi_object_append_null_reference (&iter_struct); + } + } + else + { + spi_object_append_null_reference (&iter_struct); + } + } + else if (role != ATSPI_ROLE_APPLICATION) + spi_object_append_null_reference (&iter_struct); + else + spi_object_append_desktop_reference (&iter_struct); + } + else + { + spi_object_append_reference (&iter_struct, parent); + } + + /* Marshall children */ + dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "(so)", + &iter_sub_array); + if (!atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS) && + !atk_state_set_contains_state (set, ATK_STATE_DEFUNCT)) + { + gint childcount, i; + + childcount = atk_object_get_n_accessible_children (obj); + for (i = 0; i < childcount; i++) + { + AtkObject *child; + + child = atk_object_ref_accessible_child (obj, i); + if (child) + { + spi_object_append_reference (&iter_sub_array, child); + g_object_unref (G_OBJECT (child)); + } + } + } + if (ATK_IS_SOCKET (obj) && atk_socket_is_occupied (ATK_SOCKET (obj))) + { + AtkSocket *socket = ATK_SOCKET (obj); + gchar *child_name, *child_path; + child_name = g_strdup (socket->embedded_plug_id); + child_path = g_utf8_strchr (child_name + 1, -1, ':'); + if (child_path) + { + DBusMessageIter iter_socket; + *(child_path++) = '\0'; + dbus_message_iter_open_container (&iter_sub_array, DBUS_TYPE_STRUCT, NULL, + &iter_socket); + dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_STRING, &child_name); + dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_OBJECT_PATH, &child_path); + dbus_message_iter_close_container (&iter_sub_array, &iter_socket); + } + g_free (child_name); + } + + dbus_message_iter_close_container (&iter_struct, &iter_sub_array); + + /* Marshall interfaces */ + dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", + &iter_sub_array); + spi_object_append_interfaces (&iter_sub_array, obj); + dbus_message_iter_close_container (&iter_struct, &iter_sub_array); + + /* Marshall name */ + name = atk_object_get_name (obj); + if (!name) + name = ""; + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); + + /* Marshall role */ + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role); + + /* Marshall description */ + desc = atk_object_get_description (obj); + if (!desc) + desc = ""; + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc); + + /* Marshall state set */ + spi_atk_state_set_to_dbus_array (set, states); + dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u", + &iter_sub_array); + for (count = 0; count < 2; count++) + { + dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32, + &states[count]); + } + dbus_message_iter_close_container (&iter_struct, &iter_sub_array); + } + dbus_message_iter_close_container (iter_array, &iter_struct); + g_object_unref (set); +} + +/*---------------------------------------------------------------------------*/ + +static void +ref_accessible_hf (gpointer key, gpointer obj_data, gpointer data) +{ + g_object_ref (key); +} + +/* For use as a GHFunc */ +static void +append_accessible_hf (gpointer key, gpointer obj_data, gpointer data) +{ + /* Make sure it isn't a hyperlink */ + if (ATK_IS_OBJECT (key)) + append_cache_item (ATK_OBJECT (key), data); +} + +static void +add_to_list_hf (gpointer key, gpointer obj_data, gpointer data) +{ + GSList **listptr = data; + *listptr = g_slist_prepend (*listptr, key); +} + +/*---------------------------------------------------------------------------*/ + +static void +emit_cache_remove (SpiCache *cache, GObject * obj) +{ + DBusMessage *message; + + if ((message = dbus_message_new_signal (SPI_CACHE_OBJECT_PATH, + ATSPI_DBUS_INTERFACE_CACHE, + "RemoveAccessible"))) + { + DBusMessageIter iter; + + dbus_message_iter_init_append (message, &iter); + + spi_object_append_reference (&iter, ATK_OBJECT (obj)); + + dbus_connection_send (spi_global_app_data->bus, message, NULL); + + dbus_message_unref (message); + } +} + +static void +emit_cache_add (SpiCache *cache, GObject * obj) +{ + AtkObject *accessible = ATK_OBJECT (obj); + DBusMessage *message; + + if ((message = dbus_message_new_signal (SPI_CACHE_OBJECT_PATH, + ATSPI_DBUS_INTERFACE_CACHE, + "AddAccessible"))) + { + DBusMessageIter iter; + + dbus_message_iter_init_append (message, &iter); + g_object_ref (accessible); + append_cache_item (accessible, &iter); + g_object_unref (accessible); + + dbus_connection_send (spi_global_app_data->bus, message, NULL); + + dbus_message_unref (message); + } +} + + +/*---------------------------------------------------------------------------*/ + +static DBusMessage * +impl_GetRoot (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + return spi_object_return_reference (message, spi_global_app_data->root); +} + +/*---------------------------------------------------------------------------*/ + +static DBusMessage * +impl_GetItems (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + DBusMessage *reply; + DBusMessageIter iter, iter_array; + GSList *pending_unrefs = NULL; + + if (bus == spi_global_app_data->bus) + spi_atk_add_client (dbus_message_get_sender (message)); + + reply = dbus_message_new_method_return (message); + + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, + SPI_CACHE_ITEM_SIGNATURE, &iter_array); + spi_cache_foreach (spi_global_cache, ref_accessible_hf, NULL); + spi_cache_foreach (spi_global_cache, append_accessible_hf, &iter_array); + spi_cache_foreach (spi_global_cache, add_to_list_hf, &pending_unrefs); + g_slist_free_full (pending_unrefs, g_object_unref); + dbus_message_iter_close_container (&iter, &iter_array); + return reply; +} + +/*---------------------------------------------------------------------------*/ + +static DRouteMethod methods[] = { + {impl_GetRoot, "GetRoot"}, + {impl_GetItems, "GetItems"}, + {NULL, NULL} +}; + +void +spi_initialize_cache (DRoutePath * path) +{ + droute_path_add_interface (path, ATSPI_DBUS_INTERFACE_CACHE, spi_org_a11y_atspi_Cache, methods, NULL); + + g_signal_connect (spi_global_cache, "object-added", + (GCallback) emit_cache_add, NULL); + + g_signal_connect (spi_global_cache, "object-removed", + (GCallback) emit_cache_remove, NULL); +}; + +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/adaptors/collection-adaptor.c b/atk-adaptor/adaptors/collection-adaptor.c new file mode 100644 index 0000000..97016b7 --- /dev/null +++ b/atk-adaptor/adaptors/collection-adaptor.c @@ -0,0 +1,1347 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2007 IBM Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* collection.c: implements the Collection interface */ + +#include <string.h> + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "bitarray.h" +#include "spi-dbus.h" +#include "accessible-stateset.h" + +#include "accessible-register.h" +#include "object.h" +#include "introspection.h" + +typedef struct _MatchRulePrivate MatchRulePrivate; +struct _MatchRulePrivate +{ + gint *states; + AtspiCollectionMatchType statematchtype; + AtkAttributeSet *attributes; + AtspiCollectionMatchType attributematchtype; + gint *roles; + AtspiCollectionMatchType rolematchtype; + gchar **ifaces; + AtspiCollectionMatchType interfacematchtype; + gboolean invert; +}; + +static gboolean +child_interface_p (AtkObject * child, gchar * repo_id) +{ + if (!strcasecmp (repo_id, "action")) + return ATK_IS_ACTION (child); + if (!strcasecmp (repo_id, "component")) + return ATK_IS_COMPONENT (child); + if (!strcasecmp (repo_id, "editabletext")) + return ATK_IS_EDITABLE_TEXT (child); + if (!strcasecmp (repo_id, "text")) + return ATK_IS_TEXT (child); + if (!strcasecmp (repo_id, "hypertext")) + return ATK_IS_HYPERTEXT (child); + if (!strcasecmp (repo_id, "image")) + return ATK_IS_IMAGE (child); + if (!strcasecmp (repo_id, "selection")) + return ATK_IS_SELECTION (child); + if (!strcasecmp (repo_id, "table")) + return ATK_IS_TABLE (child); + if (!strcasecmp (repo_id, "value")) + return ATK_IS_VALUE (child); + if (!strcasecmp (repo_id, "streamablecontent")) + return ATK_IS_STREAMABLE_CONTENT (child); + if (!strcasecmp (repo_id, "document")) + return ATK_IS_DOCUMENT (child); + return FALSE; +} + +#define child_collection_p(ch) (TRUE) + +static gboolean +match_states_all_p (AtkObject * child, gint * set) +{ + AtkStateSet *chs; + gint i; + gboolean ret = TRUE; + + if (set == NULL || set[0] == BITARRAY_SEQ_TERM) + return TRUE; + + chs = atk_object_ref_state_set (child); + + // TODO: use atk-state_set_contains_states; would be more efficient + for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++) + { + if (!atk_state_set_contains_state (chs, set[i])) + { + ret = FALSE; + break; + } + } + + g_object_unref (chs); + return ret; +} + +static gboolean +match_states_any_p (AtkObject * child, gint * set) +{ + AtkStateSet *chs; + gint i; + gboolean ret = FALSE; + + if (set == NULL || set[0] == BITARRAY_SEQ_TERM) + return TRUE; + + chs = atk_object_ref_state_set (child); + + for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++) + { + if (atk_state_set_contains_state (chs, set[i])) + { + ret = TRUE; + break; + } + } + + g_object_unref (chs); + return ret; +} + +static gboolean +match_states_none_p (AtkObject * child, gint * set) +{ + AtkStateSet *chs; + gint i; + gboolean ret = TRUE; + + if (set == NULL || set[0] == BITARRAY_SEQ_TERM) + return TRUE; + + chs = atk_object_ref_state_set (child); + + for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++) + { + if (atk_state_set_contains_state (chs, set[i])) + { + ret = FALSE; + break; + } + } + + g_object_unref (chs); + return ret; +} + +// TODO: need to convert at-spi roles/states to atk roles/states */ +static gboolean +match_states_lookup (AtkObject * child, MatchRulePrivate * mrp) +{ + switch (mrp->statematchtype) + { + case ATSPI_Collection_MATCH_ALL: + if (match_states_all_p (child, mrp->states)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_ANY: + if (match_states_any_p (child, mrp->states)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_NONE: + if (match_states_none_p (child, mrp->states)) + return TRUE; + break; + + default: + break; + } + + return FALSE; +} + +// TODO: Map at-spi -> atk roles at mrp creation instead of doing this; +// would be more efficient +#define spi_get_role(obj) spi_accessible_role_from_atk_role(atk_object_get_role(obj)) + +static gboolean +match_roles_all_p (AtkObject * child, gint * roles) +{ + if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) + return TRUE; + else if (roles[1] != BITARRAY_SEQ_TERM) + return FALSE; + + return (atk_object_get_role (child) == roles[0]); + +} + +static gboolean +match_roles_any_p (AtkObject * child, gint * roles) +{ + AtspiRole role; + int i; + + if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) + return TRUE; + + role = spi_accessible_role_from_atk_role (atk_object_get_role (child)); + + for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++) + if (role == roles[i]) + return TRUE; + + return FALSE; +} + +static gboolean +match_roles_none_p (AtkObject * child, gint * roles) +{ + AtkRole role; + int i; + + if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) + return TRUE; + + role = atk_object_get_role (child); + + for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++) + if (role == roles[i]) + return FALSE; + + return TRUE; +} + +static gboolean +match_roles_lookup (AtkObject * child, MatchRulePrivate * mrp) +{ + switch (mrp->rolematchtype) + { + case ATSPI_Collection_MATCH_ALL: + if (match_roles_all_p (child, mrp->roles)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_ANY: + if (match_roles_any_p (child, mrp->roles)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_NONE: + if (match_roles_none_p (child, mrp->roles)) + return TRUE; + break; + + default: + break; + + } + return FALSE; +} + +static gboolean +match_interfaces_all_p (AtkObject * obj, gchar ** ifaces) +{ + gint i; + + if (ifaces == NULL) + return TRUE; + + for (i = 0; ifaces[i]; i++) + if (!child_interface_p (obj, ifaces[i])) + { + return FALSE; + } + return TRUE; +} + +static gboolean +match_interfaces_any_p (AtkObject * obj, gchar ** ifaces) +{ + gint i; + + if (ifaces == NULL) + return TRUE; + + + for (i = 0; ifaces[i]; i++) + if (child_interface_p (obj, ifaces[i])) + { + return TRUE; + } + return FALSE; +} + +static gboolean +match_interfaces_none_p (AtkObject * obj, gchar ** ifaces) +{ + gint i; + + for (i = 0; ifaces[i]; i++) + if (child_interface_p (obj, ifaces[i])) + return FALSE; + + return TRUE; +} + +static gboolean +match_interfaces_lookup (AtkObject * child, MatchRulePrivate * mrp) +{ + switch (mrp->interfacematchtype) + { + + case ATSPI_Collection_MATCH_ALL: + if (match_interfaces_all_p (child, mrp->ifaces)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_ANY: + if (match_interfaces_any_p (child, mrp->ifaces)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_NONE: + if (match_interfaces_none_p (child, mrp->ifaces)) + return TRUE; + break; + + default: + break; + } + + return FALSE; +} + +#define split_attributes(attributes) (g_strsplit (attributes, ";", 0)) + +static gboolean +match_attributes_all_p (AtkObject * child, AtkAttributeSet * attributes) +{ + int i, k; + AtkAttributeSet *oa; + gint length, oa_length; + gboolean flag = FALSE; + + if (attributes == NULL || g_slist_length (attributes) == 0) + return TRUE; + + oa = atk_object_get_attributes (child); + length = g_slist_length (attributes); + oa_length = g_slist_length (oa); + + for (i = 0; i < length; i++) + { + AtkAttribute *attr = g_slist_nth_data (attributes, i); + for (k = 0; k < oa_length; k++) + { + AtkAttribute *oa_attr = g_slist_nth_data (attributes, i); + if (!g_ascii_strcasecmp (oa_attr->name, attr->name) && + !g_ascii_strcasecmp (oa_attr->value, attr->value)) + { + flag = TRUE; + break; + } + else + flag = FALSE; + } + if (!flag) + { + atk_attribute_set_free (oa); + return FALSE; + } + } + atk_attribute_set_free (oa); + return TRUE; +} + +static gboolean +match_attributes_any_p (AtkObject * child, AtkAttributeSet * attributes) +{ + int i, k; + + AtkAttributeSet *oa; + gint length, oa_length; + + length = g_slist_length (attributes); + if (length == 0) + return TRUE; + + oa = atk_object_get_attributes (child); + oa_length = g_slist_length (oa); + + for (i = 0; i < length; i++) + { + AtkAttribute *attr = g_slist_nth_data (attributes, i); + for (k = 0; k < oa_length; k++) + { + AtkAttribute *oa_attr = g_slist_nth_data (oa, k); + if (!g_ascii_strcasecmp (oa_attr->name, attr->name) && + !g_ascii_strcasecmp (oa_attr->value, attr->value)) + { + atk_attribute_set_free (oa); + return TRUE; + } + } + } + atk_attribute_set_free (oa); + return FALSE; +} + +static gboolean +match_attributes_none_p (AtkObject * child, AtkAttributeSet * attributes) +{ + int i, k; + + AtkAttributeSet *oa; + gint length, oa_length; + + length = g_slist_length (attributes); + if (length == 0) + return TRUE; + + oa = atk_object_get_attributes (child); + oa_length = g_slist_length (oa); + + for (i = 0; i < length; i++) + { + AtkAttribute *attr = g_slist_nth_data (attributes, i); + for (k = 0; k < oa_length; k++) + { + AtkAttribute *oa_attr = g_slist_nth_data (attributes, i); + if (!g_ascii_strcasecmp (oa_attr->name, attr->name) && + !g_ascii_strcasecmp (oa_attr->value, attr->value)) + { + atk_attribute_set_free (oa); + return FALSE; + } + } + } + atk_attribute_set_free (oa); + return TRUE; +} + +static gboolean +match_attributes_lookup (AtkObject * child, MatchRulePrivate * mrp) +{ + switch (mrp->attributematchtype) + { + + case ATSPI_Collection_MATCH_ALL: + if (match_attributes_all_p (child, mrp->attributes)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_ANY: + if (match_attributes_any_p (child, mrp->attributes)) + return TRUE; + break; + + case ATSPI_Collection_MATCH_NONE: + if (match_attributes_none_p (child, mrp->attributes)) + return TRUE; + break; + + default: + break; + } + return FALSE; +} + +static gboolean +traverse_p (AtkObject * child, const gboolean traverse) +{ + if (traverse) + return TRUE; + else + return !child_collection_p (child); +} + +static int +sort_order_canonical (MatchRulePrivate * mrp, GList * ls, + gint kount, gint max, + AtkObject * obj, glong index, gboolean flag, + AtkObject * pobj, gboolean recurse, gboolean traverse) +{ + gint i = index; + glong acount = atk_object_get_n_accessible_children (obj); + gboolean prev = pobj ? TRUE : FALSE; + + for (; i < acount && (max == 0 || kount < max); i++) + { + AtkObject *child = atk_object_ref_accessible_child (obj, i); + + g_object_unref (child); + if (prev && child == pobj) + { + return kount; + } + + if (flag && match_interfaces_lookup (child, mrp) + && match_states_lookup (child, mrp) + && match_roles_lookup (child, mrp) + && match_attributes_lookup (child, mrp)) + { + + ls = g_list_append (ls, child); + kount++; + } + + if (!flag) + flag = TRUE; + + if (recurse && traverse_p (child, traverse)) + kount = sort_order_canonical (mrp, ls, kount, + max, child, 0, TRUE, + pobj, recurse, traverse); + } + return kount; +} + +static int +sort_order_rev_canonical (MatchRulePrivate * mrp, GList * ls, + gint kount, gint max, + AtkObject * obj, gboolean flag, AtkObject * pobj) +{ + AtkObject *nextobj; + AtkObject *parent; + glong indexinparent; + + /* This breaks us out of the recursion. */ + if (!obj || obj == pobj) + { + return kount; + } + + /* Add to the list if it matches */ + if (flag && match_interfaces_lookup (obj, mrp) + && match_states_lookup (obj, mrp) + && match_roles_lookup (obj, mrp) && match_attributes_lookup (obj, mrp) + && (max == 0 || kount < max)) + { + ls = g_list_append (ls, obj); + kount++; + } + + if (!flag) + flag = TRUE; + + /* Get the current nodes index in it's parent and the parent object. */ + indexinparent = atk_object_get_index_in_parent (obj); + parent = atk_object_get_parent (obj); + + if (indexinparent > 0 && (max == 0 || kount < max)) + { + /* there are still some siblings to visit so get the previous sibling + and get it's last descendant. + First, get the previous sibling */ + nextobj = atk_object_ref_accessible_child (parent, indexinparent - 1); + g_object_unref (nextobj); + + /* Now, drill down the right side to the last descendant */ + while (atk_object_get_n_accessible_children (nextobj) > 0) + { + nextobj = atk_object_ref_accessible_child (nextobj, + atk_object_get_n_accessible_children + (nextobj) - 1); + g_object_unref (nextobj); + } + /* recurse with the last descendant */ + kount = sort_order_rev_canonical (mrp, ls, kount, max, + nextobj, TRUE, pobj); + } + else if (max == 0 || kount < max) + { + /* no more siblings so next node must be the parent */ + kount = sort_order_rev_canonical (mrp, ls, kount, max, + parent, TRUE, pobj); + + } + return kount; +} + +static int +query_exec (MatchRulePrivate * mrp, AtspiCollectionSortOrder sortby, + GList * ls, gint kount, gint max, + AtkObject * obj, glong index, + gboolean flag, + AtkObject * pobj, gboolean recurse, gboolean traverse) +{ + switch (sortby) + { + case ATSPI_Collection_SORT_ORDER_CANONICAL: + kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag, + pobj, recurse, traverse); + break; + case ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL: + kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag, + pobj, recurse, traverse); + break; + default: + kount = 0; + g_warning ("Sort method not implemented yet"); + break; + } + + return kount; +} + +static gboolean +bitarray_to_seq (dbus_uint32_t *array, int array_count, int **ret) +{ + int out_size = 4; + int out_count = 0; + int i, j; + int *out = (int *) g_malloc (out_size * sizeof (int)); + + if (!out) + return FALSE; + for (i = 0; i < array_count; i++) + { + for (j = 0; j < 32; j++) + { + if (array[i] & (1 << j)) + { + if (out_count == out_size - 2) + { + out_size <<= 1; + out = (int *) g_realloc (out, out_size * sizeof (int)); + if (!out) + return FALSE; + } + out[out_count++] = i * 32 + j; + } + } + } + out[out_count] = BITARRAY_SEQ_TERM; + *ret = out; + return TRUE; +} + + +static dbus_bool_t +read_mr (DBusMessageIter * iter, MatchRulePrivate * mrp) +{ + DBusMessageIter iter_struct, iter_array, iter_dict, iter_dict_entry; + dbus_uint32_t *array; + dbus_int32_t matchType; + int array_count; + AtkAttribute *attr; + int i; + + dbus_message_iter_recurse (iter, &iter_struct); + + /* states */ + dbus_message_iter_recurse (&iter_struct, &iter_array); + dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count); + bitarray_to_seq (array, array_count, &mrp->states); + for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++) + { + mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]); + } + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &matchType); + dbus_message_iter_next (&iter_struct); + mrp->statematchtype = matchType;; + + /* attributes */ + mrp->attributes = NULL; + dbus_message_iter_recurse (&iter_struct, &iter_dict); + while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID) + { + const char *key, *val; + const char *p, *q; + dbus_message_iter_recurse (&iter_dict, &iter_dict_entry); + dbus_message_iter_get_basic (&iter_dict_entry, &key); + dbus_message_iter_next (&iter_dict_entry); + dbus_message_iter_get_basic (&iter_dict_entry, &val); + p = q = val; + for (;;) + { + if (*q == '\0' || (*q == ':' && (q == val || q[-1] != '\\'))) + { + char *tmp; + attr = g_new (AtkAttribute, 1); + attr->name = g_strdup (key); + attr->value = g_strdup (p); + attr->value[q - p] = '\0'; + tmp = attr->value; + while (*tmp != '\0') + { + if (*tmp == '\\') + memmove (tmp, tmp + 1, strlen (tmp)); + else + tmp++; + } + mrp->attributes = g_slist_prepend (mrp->attributes, attr); + if (*q == '\0') + break; + else + p = ++q; + } + else + q++; + } + dbus_message_iter_next (&iter_dict); + } + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &matchType); + mrp->attributematchtype = matchType;; + dbus_message_iter_next (&iter_struct); + + /* Get roles and role match */ + dbus_message_iter_recurse (&iter_struct, &iter_array); + dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count); + bitarray_to_seq (array, array_count, &mrp->roles); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &matchType); + mrp->rolematchtype = matchType;; + dbus_message_iter_next (&iter_struct); + + /* Get interfaces and interface match */ + dbus_message_iter_recurse (&iter_struct, &iter_array); + mrp->ifaces = g_new0 (gchar *, 16); + i = 0; + while (i < 15 && dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + char *iface; + dbus_message_iter_get_basic (&iter_array, &iface); + mrp->ifaces [i] = g_strdup (iface); + i++; + dbus_message_iter_next (&iter_array); + } + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &matchType); + mrp->interfacematchtype = matchType;; + dbus_message_iter_next (&iter_struct); + /* get invert */ + dbus_message_iter_get_basic (&iter_struct, &mrp->invert); + dbus_message_iter_next (iter); + return TRUE; +} + +static DBusMessage * +return_and_free_list (DBusMessage * message, GList * ls) +{ + DBusMessage *reply; + DBusMessageIter iter, iter_array; + GList *item; + + reply = dbus_message_new_method_return (message); + if (!reply) + return NULL; + dbus_message_iter_init_append (reply, &iter); + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array)) + goto oom; + for (item = ls; item; item = g_list_next (item)) + { + spi_object_append_reference (&iter_array, ATK_OBJECT (item->data)); + } + if (!dbus_message_iter_close_container (&iter, &iter_array)) + goto oom; + g_list_free (ls); + return reply; +oom: + // TODO: Handle out of memory + g_list_free (ls); + return reply; +} + +static void +free_mrp_data (MatchRulePrivate * mrp) +{ + g_free (mrp->states); + atk_attribute_set_free (mrp->attributes); + g_free (mrp->roles); + g_strfreev (mrp->ifaces); +} + +static DBusMessage * +GetMatchesFrom (DBusMessage * message, + AtkObject * current_object, + MatchRulePrivate * mrp, + const AtspiCollectionSortOrder sortby, + const dbus_bool_t isrestrict, + dbus_int32_t count, const dbus_bool_t traverse) +{ + GList *ls = NULL; + AtkObject *parent; + glong index = atk_object_get_index_in_parent (current_object); + + ls = g_list_append (ls, current_object); + + if (!isrestrict) + { + parent = atk_object_get_parent (current_object); + query_exec (mrp, sortby, ls, 0, count, parent, index, + FALSE, NULL, TRUE, traverse); + } + else + query_exec (mrp, sortby, ls, 0, count, + current_object, 0, FALSE, NULL, TRUE, traverse); + + ls = g_list_remove (ls, ls->data); + + if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL) + ls = g_list_reverse (ls); + + free_mrp_data (mrp); + return return_and_free_list (message, ls); +} + +/* + inorder traversal from a given object in the hierarchy +*/ + +static int +inorder (AtkObject * collection, MatchRulePrivate * mrp, + GList * ls, gint kount, gint max, + AtkObject * obj, + gboolean flag, AtkObject * pobj, dbus_bool_t traverse) +{ + int i = 0; + + /* First, look through the children recursively. */ + kount = sort_order_canonical (mrp, ls, kount, max, obj, 0, TRUE, + NULL, TRUE, TRUE); + + /* Next, we look through the right subtree */ + while ((max == 0 || kount < max) && obj && obj != collection) + { + AtkObject *parent = atk_object_get_parent (obj); + i = atk_object_get_index_in_parent (obj); + kount = sort_order_canonical (mrp, ls, kount, max, parent, + i + 1, TRUE, FALSE, TRUE, TRUE); + obj = parent; + } + + if (max == 0 || kount < max) + { + kount = sort_order_canonical (mrp, ls, kount, max, + obj, i + 1, TRUE, FALSE, TRUE, TRUE); + } + + return kount; +} + +/* + GetMatchesInOrder: get matches from a given object in an inorder traversal. +*/ + +static DBusMessage * +GetMatchesInOrder (DBusMessage * message, + AtkObject * current_object, + MatchRulePrivate * mrp, + const AtspiCollectionSortOrder sortby, + const dbus_bool_t recurse, + dbus_int32_t count, const dbus_bool_t traverse) +{ + GList *ls = NULL; + AtkObject *obj; + + ls = g_list_append (ls, current_object); + + obj = ATK_OBJECT(spi_register_path_to_object (spi_global_register, dbus_message_get_path (message))); + + inorder (obj, mrp, ls, 0, count, + current_object, TRUE, NULL, traverse); + + ls = g_list_remove (ls, ls->data); + + if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL) + ls = g_list_reverse (ls); + + free_mrp_data (mrp); + return return_and_free_list (message, ls); +} + +/* + GetMatchesInBackOrder: get matches from a given object in a + reverse order traversal. +*/ + +static DBusMessage * +GetMatchesInBackOrder (DBusMessage * message, + AtkObject * current_object, + MatchRulePrivate * mrp, + const AtspiCollectionSortOrder sortby, + dbus_int32_t count) +{ + GList *ls = NULL; + AtkObject *collection; + + ls = g_list_append (ls, current_object); + + collection = ATK_OBJECT(spi_register_path_to_object (spi_global_register, dbus_message_get_path (message))); + + sort_order_rev_canonical (mrp, ls, 0, count, current_object, + FALSE, collection); + + ls = g_list_remove (ls, ls->data); + + if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL) + ls = g_list_reverse (ls); + + free_mrp_data (mrp); + return return_and_free_list (message, ls); +} + +static DBusMessage * +GetMatchesTo (DBusMessage * message, + AtkObject * current_object, + MatchRulePrivate * mrp, + const AtspiCollectionSortOrder sortby, + const dbus_bool_t recurse, + const dbus_bool_t isrestrict, + dbus_int32_t count, const dbus_bool_t traverse) +{ + GList *ls = NULL; + AtkObject *obj; + ls = g_list_append (ls, current_object); + + if (recurse) + { + obj = ATK_OBJECT (atk_object_get_parent (current_object)); + query_exec (mrp, sortby, ls, 0, count, + obj, 0, TRUE, current_object, TRUE, traverse); + } + else + { + obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message))); + query_exec (mrp, sortby, ls, 0, count, + obj, 0, TRUE, current_object, TRUE, traverse); + + } + + ls = g_list_remove (ls, ls->data); + + if (sortby != ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL) + ls = g_list_reverse (ls); + + free_mrp_data (mrp); + return return_and_free_list (message, ls); +} + +static DBusMessage * +impl_GetMatchesFrom (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + char *current_object_path = NULL; + AtkObject *current_object; + DBusMessageIter iter; + MatchRulePrivate rule; + dbus_uint32_t sortby; + dbus_uint32_t tree; + dbus_int32_t count; + dbus_bool_t traverse; + const char *signature; + + signature = dbus_message_get_signature (message); + if (strcmp (signature, "o(aiia{ss}iaiiasib)uuib") != 0) + { + return droute_invalid_arguments_error (message); + } + + dbus_message_iter_init (message, &iter); + dbus_message_iter_get_basic (&iter, ¤t_object_path); + current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path)); + if (!current_object) + { + // TODO: object-not-found error + return spi_dbus_general_error (message); + } + dbus_message_iter_next (&iter); + if (!read_mr (&iter, &rule)) + { + return spi_dbus_general_error (message); + } + dbus_message_iter_get_basic (&iter, &sortby); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &tree); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &count); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &traverse); + dbus_message_iter_next (&iter); + + switch (tree) + { + case ATSPI_Collection_TREE_RESTRICT_CHILDREN: + return GetMatchesFrom (message, current_object, + &rule, sortby, TRUE, count, traverse); + break; + case ATSPI_Collection_TREE_RESTRICT_SIBLING: + return GetMatchesFrom (message, current_object, + &rule, sortby, FALSE, count, traverse); + break; + case ATSPI_Collection_TREE_INORDER: + return GetMatchesInOrder (message, current_object, + &rule, sortby, TRUE, count, traverse); + break; + default: + return NULL; + } +} + +static DBusMessage * +impl_GetMatchesTo (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + char *current_object_path = NULL; + AtkObject *current_object; + DBusMessageIter iter; + MatchRulePrivate rule; + dbus_uint32_t sortby; + dbus_uint32_t tree; + dbus_bool_t recurse; + dbus_int32_t count; + dbus_bool_t traverse; + const char *signature; + + signature = dbus_message_get_signature (message); + if (strcmp (signature, "o(aiia{ss}iaiiasib)uubib") != 0) + { + return droute_invalid_arguments_error (message); + } + + dbus_message_iter_init (message, &iter); + dbus_message_iter_get_basic (&iter, ¤t_object_path); + current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path)); + if (!current_object) + { + // TODO: object-not-found error + return spi_dbus_general_error (message); + } + dbus_message_iter_next (&iter); + if (!read_mr (&iter, &rule)) + { + return spi_dbus_general_error (message); + } + dbus_message_iter_get_basic (&iter, &sortby); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &tree); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &recurse); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &count); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &traverse); + dbus_message_iter_next (&iter); + + switch (tree) + { + case ATSPI_Collection_TREE_RESTRICT_CHILDREN: + return GetMatchesTo (message, current_object, + &rule, sortby, recurse, TRUE, count, traverse); + break; + case ATSPI_Collection_TREE_RESTRICT_SIBLING: + return GetMatchesTo (message, current_object, + &rule, sortby, recurse, FALSE, count, traverse); + break; + case ATSPI_Collection_TREE_INORDER: + return GetMatchesInBackOrder (message, current_object, + &rule, sortby, count); + break; + default: + return NULL; + } +} + +static void +append_accessible_properties (DBusMessageIter *iter, AtkObject *obj, + GArray *properties) +{ + DBusMessageIter iter_struct, iter_dict, iter_dict_entry; + AtkStateSet *set; + gint i; + gint count; + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &iter_struct); + spi_object_append_reference (&iter_struct, obj); + dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_dict); + if (properties && properties->len) + { + gint i; + for (i = 0; i < properties->len; i++) + { + gchar *prop = g_array_index (properties, char *, i); + DRoutePropertyFunction func; + GType type; + func = _atk_bridge_find_property_func (prop, &type); + if (func && G_TYPE_CHECK_INSTANCE_TYPE (obj, type)) + { + dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY, + NULL, &iter_dict_entry); + dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop); + func (&iter_dict_entry, obj); + dbus_message_iter_close_container (&iter_dict, &iter_dict_entry); + } + } + } + else + { + GHashTableIter hi; + gpointer key, value; + g_hash_table_iter_init (&hi, spi_global_app_data->property_hash); + while (g_hash_table_iter_next (&hi, &key, &value)) + { + const DRouteProperty *prop = value; + GType type = _atk_bridge_type_from_iface (key); + if (!G_TYPE_CHECK_INSTANCE_TYPE (obj, type)) + continue; + for (;prop->name; prop++) + { + const char *p = key + strlen (key); + gchar *property_name; + while (p[-1] != '.') + p--; + if (!strcmp (p, "Accessible")) + property_name = g_strdup (prop->name); + else + property_name = g_strconcat (p, ".", prop->name, NULL); + dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY, + NULL, &iter_dict_entry); + dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &property_name); + g_free (property_name); + prop->get (&iter_dict_entry, obj); + dbus_message_iter_close_container (&iter_dict, &iter_dict_entry); + } + } + } + dbus_message_iter_close_container (&iter_struct, &iter_dict); + dbus_message_iter_close_container (iter, &iter_struct); + + set = atk_object_ref_state_set (obj); + if (set) + { + gboolean md = atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS); + g_object_unref (set); + if (md) + return; + } + count = atk_object_get_n_accessible_children (obj); + for (i = 0; i < count; i++) + { + AtkObject *child = atk_object_ref_accessible_child (obj, i); + if (child) + { + append_accessible_properties (iter, child, properties); + g_object_unref (child); + } + } +} + +static void +skip (const char **p) +{ + const char *sig = *p; + gint nest = (*sig != 'a'); + + sig++; + while (*sig) + { + if (*sig == '(' || *sig == '{') + nest++; + else if (*sig == ')' || *sig == '}') + nest--; + sig++; + } + *p = sig; +} + +static gboolean +types_match (DBusMessageIter *iter, char c) +{ + char t = dbus_message_iter_get_arg_type (iter); + + if (t == 'r' && c == '(') + return TRUE; + else if (t != c) + return FALSE; + + return FALSE; +} + +static void +walk (DBusMessageIter *iter, const char *sig, gboolean array) +{ + while (*sig && *sig != ')' && *sig != '}') + { + if (array && dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_INVALID) + break; + if (!types_match (iter, *sig)) + { + g_error ("Expected %s, got %c", sig, dbus_message_iter_get_arg_type (iter)); + } + switch (*sig) + { + case 's': + { + const char *str; + DBusError error; + dbus_error_init (&error); + dbus_message_iter_get_basic (iter, &str); + g_print ("%s\n", str); + if (!dbus_validate_utf8 (str, &error)) + g_error ("Bad UTF-8 string"); + } + break; + case 'a': + { + DBusMessageIter iter_array; + dbus_message_iter_recurse (iter, &iter_array); + walk (&iter_array, sig + 1, TRUE); + skip (&sig); + } + break; + case DBUS_TYPE_STRUCT: + case DBUS_TYPE_DICT_ENTRY: + { + DBusMessageIter iter_struct; + dbus_message_iter_recurse (iter, &iter_struct); + walk (&iter_struct, sig + 1, FALSE); + skip (&sig); + } + } + dbus_message_iter_next (iter); + if (!array) + sig++; + } + if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_INVALID) + g_error ("Unexpected data '%c'", dbus_message_iter_get_arg_type (iter)); +} + +static void +walkm (DBusMessage *message) +{ + DBusMessageIter iter; + const char *sig = dbus_message_get_signature (message); + + g_print ("sig: %s\n", sig); + dbus_message_iter_init (message, &iter); + walk (&iter, sig, FALSE); +} + +static DBusMessage * +impl_GetTree (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + DBusMessage *reply; + DBusMessageIter iter, iter_array; + MatchRulePrivate rule; + GArray *properties; + + g_return_val_if_fail (ATK_IS_OBJECT (user_data), + droute_not_yet_handled_error (message)); + + if (strcmp (dbus_message_get_signature (message), "(aiia{ss}iaiiasib)as") != 0) + return droute_invalid_arguments_error (message); + + properties = g_array_new (TRUE, TRUE, sizeof (char *)); + dbus_message_iter_init (message, &iter); + if (!read_mr (&iter, &rule)) + { + return spi_dbus_general_error (message); + } + + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + const char *prop; + dbus_message_iter_get_basic (&iter_array, &prop); + g_array_append_val (properties, prop); + dbus_message_iter_next (&iter_array); + } + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "((so)a{sv})", + &iter_array); + append_accessible_properties (&iter_array, object, properties); + dbus_message_iter_close_container (&iter, &iter_array); + } +//walkm (reply); + return reply; +} + +static DBusMessage * +impl_GetMatches (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkObject *obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message))); + DBusMessageIter iter; + MatchRulePrivate rule; + dbus_uint32_t sortby; + dbus_int32_t count; + dbus_bool_t traverse; + GList *ls = NULL; + const char *signature; + + signature = dbus_message_get_signature (message); + if (strcmp (signature, "(aiia{ss}iaiiasib)uib") != 0) + { + return droute_invalid_arguments_error (message); + } + + dbus_message_iter_init (message, &iter); + if (!read_mr (&iter, &rule)) + { + return spi_dbus_general_error (message); + } + dbus_message_iter_get_basic (&iter, &sortby); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &count); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &traverse); + dbus_message_iter_next (&iter); + ls = g_list_prepend (ls, obj); + count = query_exec (&rule, sortby, ls, 0, count, + obj, 0, TRUE, NULL, TRUE, traverse); + ls = g_list_remove (ls, ls->data); + + if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL) + ls = g_list_reverse (ls); + free_mrp_data (&rule); + return return_and_free_list (message, ls); +} + +static DRouteMethod methods[] = { + {impl_GetMatchesFrom, "GetMatchesFrom"}, + {impl_GetMatchesTo, "GetMatchesTo"}, + {impl_GetTree, "GetTree"}, + {impl_GetMatches, "GetMatches"}, + {NULL, NULL} +}; + +void +spi_initialize_collection (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_COLLECTION, spi_org_a11y_atspi_Collection, methods, NULL); +}; diff --git a/atk-adaptor/adaptors/component-adaptor.c b/atk-adaptor/adaptors/component-adaptor.c new file mode 100644 index 0000000..95192e1 --- /dev/null +++ b/atk-adaptor/adaptors/component-adaptor.c @@ -0,0 +1,499 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" +#include <string.h> + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static DBusMessage * +impl_Contains (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_int32_t x, y; + dbus_uint32_t coord_type; + dbus_bool_t retval; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, + DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + retval = + atk_component_contains (component, x, y, (AtkCoordType) coord_type); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &retval, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetAccessibleAtPoint (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_int32_t x, y; + dbus_uint32_t coord_type; + DBusMessage *reply; + AtkObject *child; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, + DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + child = + atk_component_ref_accessible_at_point (component, x, y, + (AtkCoordType) coord_type); + reply = spi_object_return_reference (message, child); + if (child) + g_object_unref (child); + + return reply; +} + +static DBusMessage * +impl_GetExtents (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_uint32_t coord_type; + gint ix, iy, iwidth, iheight; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_component_get_extents (component, &ix, &iy, &iwidth, &iheight, + (AtkCoordType) coord_type); + return spi_dbus_return_rect (message, ix, iy, iwidth, iheight); +} + +static DBusMessage * +impl_GetPosition (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_uint32_t coord_type; + gint ix = 0, iy = 0; + dbus_int32_t x, y; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_component_get_position (component, &ix, &iy, (AtkCoordType) coord_type); + x = ix; + y = iy; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, + &y, DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetSize (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + gint iwidth = 0, iheight = 0; + dbus_int32_t width, height; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + atk_component_get_size (component, &iwidth, &iheight); + width = iwidth; + height = iheight; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &width, + DBUS_TYPE_INT32, &height, DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetLayer (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + AtkLayer atklayer; + dbus_uint32_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + atklayer = atk_component_get_layer (component); + + switch (atklayer) + { + case ATK_LAYER_BACKGROUND: + rv = ATSPI_LAYER_BACKGROUND; + break; + case ATK_LAYER_CANVAS: + rv = ATSPI_LAYER_CANVAS; + break; + case ATK_LAYER_WIDGET: + rv = ATSPI_LAYER_WIDGET; + break; + case ATK_LAYER_MDI: + rv = ATSPI_LAYER_MDI; + break; + case ATK_LAYER_POPUP: + rv = ATSPI_LAYER_POPUP; + break; + case ATK_LAYER_OVERLAY: + rv = ATSPI_LAYER_OVERLAY; + break; + case ATK_LAYER_WINDOW: + rv = ATSPI_LAYER_WINDOW; + break; + default: + rv = ATSPI_LAYER_INVALID; + break; + } + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetMDIZOrder (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_int16_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + rv = atk_component_get_mdi_zorder (component); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT16, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GrabFocus (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + rv = atk_component_grab_focus (component); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GrabHighlight (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + rv = atk_component_grab_highlight (component); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_ClearHighlight (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + rv = atk_component_clear_highlight (component); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +#if 0 +static DBusMessage * +impl_registerFocusHandler (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ +} + +static DBusMessage * +impl_deregisterFocusHandler (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ +} +#endif + +static DBusMessage * +impl_GetAlpha (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + double rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + rv = atk_component_get_alpha (component); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_DOUBLE, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_SetExtents (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + DBusMessageIter iter, iter_struct; + dbus_uint32_t coord_type; + dbus_int32_t x, y, width, height; + dbus_bool_t ret; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (strcmp (dbus_message_get_signature (message), "(iiii)u") != 0) + { + return droute_invalid_arguments_error (message); + } + + dbus_message_iter_init (message, &iter); + dbus_message_iter_recurse (&iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &x); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &y); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &width); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &height); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &coord_type); + + ret = atk_component_set_extents (component, x, y, width, height, coord_type); + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + + return reply; +} + +static DBusMessage * +impl_SetPosition (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_uint32_t coord_type; + dbus_int32_t x, y; + dbus_bool_t ret; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, + DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + ret = atk_component_set_position (component, x, y, coord_type); + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + + return reply; +} + +static dbus_bool_t +impl_get_ScreenExtents (DBusMessageIter * iter, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + DBusMessageIter iter_variant, iter_struct; + gint ix = -1, iy = -1, iwidth = -1, iheight = -1; + dbus_uint32_t x, y, width, height; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), FALSE); + + atk_component_get_extents (component, &ix, &iy, &iwidth, &iheight, ATK_XY_SCREEN); + x = ix; + y = iy; + width = iwidth; + height = iheight; + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(uuuu)", + &iter_variant); + dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL, + &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &x); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &y); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &width); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &height); + dbus_message_iter_close_container (&iter_variant, &iter_struct); + dbus_message_iter_close_container (iter, &iter_variant); + return TRUE; +} + +static dbus_bool_t +impl_get_HighlightIndex (DBusMessageIter * iter, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), -1); + return droute_return_v_int32 (iter, atk_component_get_highlight_index (component)); +} + +static DBusMessage * +impl_SetSize (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkComponent *component = (AtkComponent *) user_data; + dbus_int32_t width, height; + dbus_bool_t ret; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_COMPONENT (user_data), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32, &height, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + ret = atk_component_set_size (component, width, height); + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + + return reply; +} + +static DRouteMethod methods[] = { + {impl_Contains, "Contains"}, + {impl_GetAccessibleAtPoint, "GetAccessibleAtPoint"}, + {impl_GetExtents, "GetExtents"}, + {impl_GetPosition, "GetPosition"}, + {impl_GetSize, "GetSize"}, + {impl_GetLayer, "GetLayer"}, + {impl_GetMDIZOrder, "GetMDIZOrder"}, + {impl_GrabFocus, "GrabFocus"}, + {impl_GrabHighlight, "GrabHighlight"}, + {impl_ClearHighlight, "ClearHighlight"}, + //{impl_registerFocusHandler, "registerFocusHandler"}, + //{impl_deregisterFocusHandler, "deregisterFocusHandler"}, + {impl_GetAlpha, "GetAlpha"}, + {impl_SetExtents, "SetExtents"}, + {impl_SetPosition, "SetPosition"}, + {impl_SetSize, "SetSize"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_ScreenExtents, NULL, "ScreenExtents"}, + {impl_get_HighlightIndex, NULL, "HighlightIndex"}, + {NULL, NULL, NULL} +}; +void +spi_initialize_component (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_COMPONENT, spi_org_a11y_atspi_Component, methods, properties); +}; diff --git a/atk-adaptor/adaptors/document-adaptor.c b/atk-adaptor/adaptors/document-adaptor.c new file mode 100644 index 0000000..020512b --- /dev/null +++ b/atk-adaptor/adaptors/document-adaptor.c @@ -0,0 +1,142 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_CurrentPageNumber (DBusMessageIter * iter, void *user_data) +{ + AtkDocument *document = (AtkDocument *) user_data; + g_return_val_if_fail (ATK_IS_DOCUMENT (user_data), FALSE); + return droute_return_v_int32 (iter, atk_document_get_current_page_number (document)); +} + +static dbus_bool_t +impl_get_PageCount (DBusMessageIter * iter, void *user_data) +{ + AtkDocument *document = (AtkDocument *) user_data; + g_return_val_if_fail (ATK_IS_DOCUMENT (user_data), FALSE); + return droute_return_v_int32 (iter, atk_document_get_page_count (document)); +} + +static DBusMessage * +impl_GetLocale (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkDocument *document = (AtkDocument *) user_data; + const gchar *lc; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_DOCUMENT (user_data), + droute_not_yet_handled_error (message)); + lc = atk_document_get_locale (document); + if (!lc) + lc = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &lc, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetAttributeValue (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkDocument *document = (AtkDocument *) user_data; + gchar *attributename; + const gchar *atr; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_DOCUMENT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_STRING, &attributename, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atr = atk_document_get_attribute_value (document, attributename); + if (!atr) + atr = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &atr, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetAttributes (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkDocument *document = (AtkDocument *) user_data; + DBusMessage *reply; + AtkAttributeSet *attributes; + DBusMessageIter iter; + + g_return_val_if_fail (ATK_IS_DOCUMENT (user_data), + droute_not_yet_handled_error (message)); + + attributes = atk_document_get_attributes (document); + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_iter_init_append (reply, &iter); + spi_object_append_attribute_set (&iter, attributes); + } + + if (attributes) + atk_attribute_set_free (attributes); + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetLocale, "GetLocale"}, + {impl_GetAttributeValue, "GetAttributeValue"}, + {impl_GetAttributes, "GetAttributes"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_CurrentPageNumber, NULL, "CurrentPageNumber"}, + {impl_get_PageCount, NULL, "PageCount"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_document (DRoutePath * path) +{ + droute_path_add_interface (path, + ATSPI_DBUS_INTERFACE_DOCUMENT, spi_org_a11y_atspi_Document, methods, properties); +}; diff --git a/atk-adaptor/adaptors/editabletext-adaptor.c b/atk-adaptor/adaptors/editabletext-adaptor.c new file mode 100644 index 0000000..2e264fc --- /dev/null +++ b/atk-adaptor/adaptors/editabletext-adaptor.c @@ -0,0 +1,207 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" +#include "introspection.h" + +#include "spi-dbus.h" + +static DBusMessage * +impl_SetTextContents (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkEditableText *editable = (AtkEditableText *) user_data; + const char *newContents; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_EDITABLE_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_STRING, &newContents, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_editable_text_set_text_contents (editable, newContents); + rv = TRUE; + // TODO decide if we really need this return value + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_InsertText (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkEditableText *editable = (AtkEditableText *) user_data; + dbus_int32_t position, length; + char *text; + dbus_bool_t rv; + DBusMessage *reply; + gint ip; + + g_return_val_if_fail (ATK_IS_EDITABLE_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &position, DBUS_TYPE_STRING, &text, + DBUS_TYPE_INT32, &length, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ip = position; + atk_editable_text_insert_text (editable, text, length, &ip); + rv = TRUE; + // TODO decide if we really need this return value + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_CopyText (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkEditableText *editable = (AtkEditableText *) user_data; + dbus_int32_t startPos, endPos; + + g_return_val_if_fail (ATK_IS_EDITABLE_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &startPos, DBUS_TYPE_INT32, &endPos, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_editable_text_copy_text (editable, startPos, endPos); + return dbus_message_new_method_return (message); +} + +static DBusMessage * +impl_CutText (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkEditableText *editable = (AtkEditableText *) user_data; + dbus_int32_t startPos, endPos; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_EDITABLE_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &startPos, DBUS_TYPE_INT32, &endPos, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_editable_text_cut_text (editable, startPos, endPos); + rv = TRUE; + // TODO decide if we really need this return value + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_DeleteText (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkEditableText *editable = (AtkEditableText *) user_data; + dbus_int32_t startPos, endPos; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_EDITABLE_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &startPos, DBUS_TYPE_INT32, &endPos, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_editable_text_delete_text (editable, startPos, endPos); + rv = TRUE; + // TODO decide if we really need this return value + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_PasteText (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkEditableText *editable = (AtkEditableText *) user_data; + dbus_int32_t position; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_EDITABLE_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &position, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_editable_text_paste_text (editable, position); + rv = TRUE; + // TODO decide if we really need this return value + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_SetTextContents, "SetTextContents"}, + {impl_InsertText, "InsertText"}, + {impl_CopyText, "CopyText"}, + {impl_CutText, "CutText"}, + {impl_DeleteText, "DeleteText"}, + {impl_PasteText, "PasteText"}, + {NULL, NULL} +}; + +void +spi_initialize_editabletext (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_EDITABLE_TEXT, spi_org_a11y_atspi_EditableText, methods, NULL); +}; diff --git a/atk-adaptor/adaptors/hyperlink-adaptor.c b/atk-adaptor/adaptors/hyperlink-adaptor.c new file mode 100644 index 0000000..0d0dc37 --- /dev/null +++ b/atk-adaptor/adaptors/hyperlink-adaptor.c @@ -0,0 +1,156 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "introspection.h" +#include "object.h" + +static AtkHyperlink * +get_hyperlink (void *user_data) +{ + if (ATK_IS_HYPERLINK (user_data)) + return ATK_HYPERLINK (user_data); + if (ATK_IS_HYPERLINK_IMPL (user_data)) + return atk_hyperlink_impl_get_hyperlink (ATK_HYPERLINK_IMPL (user_data)); + return NULL; +} + +static dbus_bool_t +impl_get_NAnchors (DBusMessageIter * iter, void *user_data) +{ + AtkHyperlink *link = get_hyperlink (user_data); + g_return_val_if_fail (ATK_IS_HYPERLINK (link), FALSE); + return droute_return_v_int32 (iter, atk_hyperlink_get_n_anchors (link)); +} + + +static dbus_bool_t +impl_get_StartIndex (DBusMessageIter * iter, void *user_data) +{ + AtkHyperlink *link = get_hyperlink (user_data); + g_return_val_if_fail (ATK_IS_HYPERLINK (link), FALSE); + return droute_return_v_int32 (iter, atk_hyperlink_get_start_index (link)); +} + +static dbus_bool_t +impl_get_EndIndex (DBusMessageIter * iter, void *user_data) +{ + AtkHyperlink *link = get_hyperlink (user_data); + g_return_val_if_fail (ATK_IS_HYPERLINK (link), FALSE); + return droute_return_v_int32 (iter, atk_hyperlink_get_end_index (link)); +} + +static DBusMessage * +impl_GetObject (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkHyperlink *link = get_hyperlink (user_data); + dbus_int32_t i; + AtkObject *atk_object; + + g_return_val_if_fail (ATK_IS_HYPERLINK (link), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_object = atk_hyperlink_get_object (link, i); + return spi_object_return_reference (message, atk_object); +} + +static DBusMessage * +impl_GetURI (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkHyperlink *link = get_hyperlink (user_data); + dbus_int32_t i; + gchar *rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_HYPERLINK (link), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + rv = atk_hyperlink_get_uri (link, i); + if (!rv) + rv = g_strdup (""); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv, + DBUS_TYPE_INVALID); + } + g_free (rv); + return reply; +} + +static DBusMessage * +impl_IsValid (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkHyperlink *link = get_hyperlink (user_data); + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_HYPERLINK (link), + droute_not_yet_handled_error (message)); + + rv = atk_hyperlink_is_valid (link); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetObject, "GetObject"}, + {impl_GetURI, "GetURI"}, + {impl_IsValid, "IsValid"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_NAnchors, NULL, "NAnchors"}, + {impl_get_StartIndex, NULL, "StartIndex"}, + {impl_get_EndIndex, NULL, "EndIndex"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_hyperlink (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_HYPERLINK, + spi_org_a11y_atspi_Hyperlink, + methods, properties); +}; diff --git a/atk-adaptor/adaptors/hypertext-adaptor.c b/atk-adaptor/adaptors/hypertext-adaptor.c new file mode 100644 index 0000000..e31266f --- /dev/null +++ b/atk-adaptor/adaptors/hypertext-adaptor.c @@ -0,0 +1,112 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "object.h" + +#include "introspection.h" + +static DBusMessage * +impl_GetNLinks (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkHypertext *hypertext = (AtkHypertext *) user_data; + gint rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_HYPERTEXT (user_data), + droute_not_yet_handled_error (message)); + rv = atk_hypertext_get_n_links (hypertext); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetLink (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkHypertext *hypertext = (AtkHypertext *) user_data; + dbus_int32_t linkIndex; + AtkHyperlink *link; + + g_return_val_if_fail (ATK_IS_HYPERTEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &linkIndex, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + link = atk_hypertext_get_link (hypertext, linkIndex); + /*The above line doesn't ref the link, and the next call is going to unref*/ + if (link) + g_object_ref (link); + return spi_hyperlink_return_reference (message, link); +} + +static DBusMessage * +impl_GetLinkIndex (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkHypertext *hypertext = (AtkHypertext *) user_data; + dbus_int32_t characterIndex; + dbus_int32_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_HYPERTEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &characterIndex, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_hypertext_get_link_index (hypertext, characterIndex); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetNLinks, "GetNLinks"}, + {impl_GetLink, "GetLink"}, + {impl_GetLinkIndex, "GetLinkIndex"}, + {NULL, NULL} +}; + +void +spi_initialize_hypertext (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_HYPERTEXT, spi_org_a11y_atspi_Hypertext, methods, NULL); +}; diff --git a/atk-adaptor/adaptors/image-adaptor.c b/atk-adaptor/adaptors/image-adaptor.c new file mode 100644 index 0000000..89ac20f --- /dev/null +++ b/atk-adaptor/adaptors/image-adaptor.c @@ -0,0 +1,140 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_ImageDescription (DBusMessageIter * iter, void *user_data) +{ + AtkImage *image = (AtkImage *) user_data; + g_return_val_if_fail (ATK_IS_IMAGE (user_data), FALSE); + return droute_return_v_string (iter, + atk_image_get_image_description (image)); +} + +static dbus_bool_t +impl_get_ImageLocale (DBusMessageIter * iter, void *user_data) +{ + AtkImage *image = (AtkImage *) user_data; + g_return_val_if_fail (ATK_IS_IMAGE (user_data), FALSE); + return droute_return_v_string (iter, atk_image_get_image_locale (image)); +} + +static DBusMessage * +impl_GetImageExtents (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkImage *image = (AtkImage *) user_data; + dbus_uint32_t coordType; + gint ix, iy, iwidth, iheight; + + g_return_val_if_fail (ATK_IS_IMAGE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_image_get_image_size (image, &iwidth, &iheight); + atk_image_get_image_position (image, &ix, &iy, (AtkCoordType) coordType); + return spi_dbus_return_rect (message, ix, iy, iwidth, iheight); +} + +static DBusMessage * +impl_GetImagePosition (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkImage *image = (AtkImage *) user_data; + dbus_uint32_t coord_type; + gint ix = 0, iy = 0; + dbus_int32_t x, y; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_IMAGE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_UINT32, &coord_type, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_image_get_image_position (image, &ix, &iy, (AtkCoordType) coord_type); + x = ix; + y = iy; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, + &y, DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetImageSize (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkImage *image = (AtkImage *) user_data; + gint iwidth = 0, iheight = 0; + dbus_int32_t width, height; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_IMAGE (user_data), + droute_not_yet_handled_error (message)); + atk_image_get_image_size (image, &iwidth, &iheight); + width = iwidth; + height = iheight; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &width, + DBUS_TYPE_INT32, &height, DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetImageExtents, "GetImageExtents"}, + {impl_GetImagePosition, "GetImagePosition"}, + {impl_GetImageSize, "GetImageSize"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_ImageDescription, NULL, "ImageDescription"}, + {impl_get_ImageLocale, NULL, "ImageLocale"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_image (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_IMAGE, spi_org_a11y_atspi_Image, methods, properties); +}; diff --git a/atk-adaptor/adaptors/selection-adaptor.c b/atk-adaptor/adaptors/selection-adaptor.c new file mode 100644 index 0000000..4776ebc --- /dev/null +++ b/atk-adaptor/adaptors/selection-adaptor.c @@ -0,0 +1,257 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_NSelectedChildren (DBusMessageIter * iter, void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + g_return_val_if_fail (ATK_IS_SELECTION (user_data), FALSE); + return droute_return_v_int32 (iter, + atk_selection_get_selection_count + (selection)); +} + +/*static char * +impl_get_NSelectedChildren_str (void *datum) +{ + g_return_val_if_fail (ATK_IS_SELECTION (user_data), FALSE); + return g_strdup_printf ("%d", + atk_selection_get_selection_count ((AtkSelection *) + datum)); +}*/ + +static DBusMessage * +impl_GetSelectedChild (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + DBusMessage *reply; + dbus_int32_t selectedChildIndex; + AtkObject *atk_object; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &selectedChildIndex, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_object = atk_selection_ref_selection (selection, selectedChildIndex); + reply = spi_object_return_reference (message, atk_object); + if (atk_object) + g_object_unref (atk_object); + + return reply; +} + +static DBusMessage * +impl_SelectChild (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + dbus_int32_t childIndex; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &childIndex, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_selection_add_selection (selection, childIndex); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_DeselectSelectedChild (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + dbus_int32_t selectedChildIndex; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &selectedChildIndex, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_selection_remove_selection (selection, selectedChildIndex); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_IsChildSelected (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + dbus_int32_t childIndex; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &childIndex, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_selection_is_child_selected (selection, childIndex); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_SelectAll (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + rv = atk_selection_select_all_selection (selection); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_ClearSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + rv = atk_selection_clear_selection (selection); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_DeselectChild (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkSelection *selection = (AtkSelection *) user_data; + dbus_int32_t selectedChildIndex; + dbus_bool_t rv = FALSE; + gint i, nselected; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_SELECTION (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &selectedChildIndex, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + nselected = atk_selection_get_selection_count (selection); + for (i = 0; i < nselected; ++i) + { + AtkObject *selected_obj = atk_selection_ref_selection (selection, i); + if (atk_object_get_index_in_parent (selected_obj) == selectedChildIndex) + { + g_object_unref (G_OBJECT (selected_obj)); + rv = atk_selection_remove_selection (selection, i); + break; + } + g_object_unref (G_OBJECT (selected_obj)); + } + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetSelectedChild, "GetSelectedChild"}, + {impl_SelectChild, "SelectChild"}, + {impl_DeselectSelectedChild, "DeselectSelectedChild"}, + {impl_IsChildSelected, "IsChildSelected"}, + {impl_SelectAll, "SelectAll"}, + {impl_ClearSelection, "ClearSelection"}, + {impl_DeselectChild, "DeselectChild"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_NSelectedChildren, NULL, "NSelectedChildren"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_selection (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_SELECTION, + spi_org_a11y_atspi_Selection, + methods, properties); +}; diff --git a/atk-adaptor/adaptors/socket-adaptor.c b/atk-adaptor/adaptors/socket-adaptor.c new file mode 100644 index 0000000..ed6faa3 --- /dev/null +++ b/atk-adaptor/adaptors/socket-adaptor.c @@ -0,0 +1,208 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> + +#include "spi-dbus.h" +#include "accessible-stateset.h" +#include "object.h" +#include "introspection.h" +#include "bridge.h" + +static DBusMessage * +new_socket_call_message (AtkComponent *component, const char *member) +{ + char *id = g_object_get_data (G_OBJECT (component), "dbus-plug-parent"); + char *bus_parent; + char *path_parent; + + if (!id) + { + g_warning ("new_socket_call_message: no id"); + return NULL; + } + bus_parent = g_strdup (id); + if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':'))) + { + DBusMessage *message; + *(path_parent++) = '\0'; + message = dbus_message_new_method_call (bus_parent, path_parent, + ATSPI_DBUS_INTERFACE_COMPONENT, + member); + g_free (bus_parent); + return message; + } + return NULL; +} + +static void +atspi_plug_component_get_extents (AtkComponent *component, gint *x, gint *y, + gint *width, gint *height, + AtkCoordType coord_type) +{ + DBusMessage *message = new_socket_call_message (component, "GetExtents"); + DBusMessage *reply; + dbus_uint32_t coord_type_dbus = coord_type; + DBusError error; + const char *signature; + DBusMessageIter iter, iter_struct; + dbus_int32_t tmp; + + dbus_error_init (&error); + dbus_message_append_args (message, DBUS_TYPE_UINT32, &coord_type_dbus, DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block (spi_global_app_data->bus, + message, -1, &error); + dbus_message_unref (message); + if (!reply) + return; + signature = dbus_message_get_signature (reply); + if (g_strcmp0 (signature, "(iiii)") != 0) + { + g_warning ("Got unexpected signature %s from GetExtents\n", signature); + dbus_message_unref (reply); + return; + } + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &tmp); + *x = tmp; + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &tmp); + *y = tmp; + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &tmp); + *width = tmp; + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &tmp); + *height = tmp; + dbus_message_unref (reply); +} + +static void +atspi_plug_component_get_position (AtkComponent *component, gint *x, gint *y, + AtkCoordType coord_type) +{ + DBusMessage *message = new_socket_call_message (component, "GetPosition"); + DBusMessage *reply; + dbus_uint32_t coord_type_dbus = coord_type; + DBusError error; + dbus_int32_t x_dbus, y_dbus; + + dbus_error_init (&error); + dbus_message_append_args (message, DBUS_TYPE_UINT32, &coord_type_dbus, DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block (spi_global_app_data->bus, + message, -1, &error); + dbus_message_unref (message); + if (!reply) + return; + if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_INT32, &x_dbus, + DBUS_TYPE_INT32, &y_dbus, DBUS_TYPE_INVALID)) + { + g_warning ("GetPosition failed: %s", error.message); + dbus_error_free (&error); + } + else + { + *x = x_dbus; + *y = y_dbus; + } + dbus_message_unref (reply); +} + +static void +atspi_plug_component_get_size (AtkComponent *component, + gint *width, gint *height) +{ + DBusMessage *message = new_socket_call_message (component, "GetSize"); + DBusMessage *reply; + DBusError error; + dbus_uint32_t width_dbus, height_dbus; + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (spi_global_app_data->bus, + message, -1, &error); + dbus_message_unref (message); + if (!reply) + return; + if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_INT32, &width_dbus, + DBUS_TYPE_INT32, &height_dbus, DBUS_TYPE_INVALID)) + { + g_warning ("GetSize failed: %s", error.message); + dbus_error_free (&error); + } + else + { + *width = width_dbus; + *height = height_dbus; + } + dbus_message_unref (reply); +} + +static DBusMessage * +impl_Embedded (DBusConnection *bus, + DBusMessage *message, + void *user_data) +{ + AtkObject *object = (AtkObject *) user_data; + char *path; + gchar *id; + + if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + id = g_strconcat (dbus_message_get_sender (message), ":", path, NULL); + g_object_set_data_full (G_OBJECT (object), "dbus-plug-parent", id, (GDestroyNotify)g_free); + + if (ATK_IS_COMPONENT (object)) + { + AtkComponent *component = ATK_COMPONENT (object); + AtkComponentIface *iface = ATK_COMPONENT_GET_IFACE (component); + iface->get_extents = atspi_plug_component_get_extents; + iface->get_size = atspi_plug_component_get_size; + iface->get_position = atspi_plug_component_get_position; + } + + /* Retrieve some info about the children, if they exist, when + embedding the plug to ensure the a11y subtree is generated. + https://bugzilla.gnome.org/show_bug.cgi?id=663876 */ + atk_object_get_n_accessible_children (object); + + return dbus_message_new_method_return (message); +} + +static DRouteMethod methods[] = { + {impl_Embedded, "Embedded"}, + {NULL, NULL} +}; + +void +spi_initialize_socket (DRoutePath * path) +{ + droute_path_add_interface (path, + ATSPI_DBUS_INTERFACE_SOCKET, + NULL, /* spi_org_a11y_atspi_Socket, */ + methods, NULL); +}; diff --git a/atk-adaptor/adaptors/streamablecontent-adaptor.c b/atk-adaptor/adaptors/streamablecontent-adaptor.c new file mode 100644 index 0000000..8192e1a --- /dev/null +++ b/atk-adaptor/adaptors/streamablecontent-adaptor.c @@ -0,0 +1,330 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* streamablecontent.c : implements the StreamableContent interface */ + +#include <config.h> +#include <stdio.h> +#include <string.h> + +#include <libspi/component.h> +#include <libspi/streamablecontent.h> + +/* Our parent Gtk object type */ +#define PARENT_TYPE SPI_TYPE_BASE + +/* A pointer to our parent object class */ +static GObjectClass *spi_streamable_parent_class; + +#define SPI_CONTENT_STREAM_TYPE (spi_content_stream_get_type ()) +#define SPI_CONTENT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPI_CONTENT_STREAM_TYPE, SpiContentStream)) +#define SPI_CONTENT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPI_CONTENT_STREAM_TYPE, SpiContentStreamClass)) +#define SPI_IS_CONTENT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPI_CONTENT_STREAM_TYPE)) +#define SPI_IS_CONTENT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPI_CONTENT_STREAM_TYPE)) + +typedef struct _SpiContentStream SpiContentStream; +typedef struct _SpiContentStreamClass SpiContentStreamClass; + +struct _SpiContentStream +{ + BonoboObject parent; + GIOChannel *gio; +}; + +struct _SpiContentStreamClass +{ + BonoboObjectClass parent_class; + POA_Accessibility_ContentStream__epv epv; +}; + +GType spi_content_stream_get_type (void); + +static SpiContentStream * +spi_content_stream_new (GIOChannel * gio) +{ + SpiContentStream *new_stream = g_object_new (SPI_CONTENT_STREAM_TYPE, NULL); + new_stream->gio = gio; + return new_stream; +} + +static void +spi_content_stream_dispose (GObject * o) +{ + if (SPI_IS_CONTENT_STREAM (o)) + { + SpiContentStream *stream = SPI_CONTENT_STREAM (o); + if (stream->gio) + g_io_channel_unref (stream->gio); + } +} + +static CORBA_long +impl_content_stream_seek (PortableServer_Servant servant, + const CORBA_long offset, + const Accessibility_ContentStream_SeekType whence, + CORBA_Environment * ev) +{ + SpiContentStream *stream = + SPI_CONTENT_STREAM (bonobo_object_from_servant (servant)); + if (stream && stream->gio) + { + GError *err; + GSeekType seektype = G_SEEK_SET; + switch (whence) + { + case Accessibility_ContentStream_SEEK_CURRENT: + seektype = G_SEEK_CUR; + break; + case Accessibility_ContentStream_SEEK_END: + seektype = G_SEEK_END; + break; + } + if (g_io_channel_seek_position (stream->gio, (gint64) offset, + seektype, &err) == G_IO_STATUS_NORMAL) + return offset; + else + return -1; + } + else + return -1; +} + +static CORBA_long +impl_content_stream_read (PortableServer_Servant servant, + const CORBA_long count, + Accessibility_ContentStream_iobuf ** buffer, + CORBA_Environment * ev) +{ + SpiContentStream *stream = + SPI_CONTENT_STREAM (bonobo_object_from_servant (servant)); + CORBA_long realcount = 0; + + if (stream && stream->gio) + { + gchar *gbuf = NULL; + GIOStatus status; + GError *err = NULL; + + /* read the giochannel and determine the actual bytes read... */ + if (count != -1) + { + gbuf = g_malloc (count + 1); + status = + g_io_channel_read_chars (stream->gio, gbuf, count, &realcount, + &err); + } + else + status = + g_io_channel_read_to_end (stream->gio, &gbuf, &realcount, &err); + + if (status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) + { + *buffer = Bonobo_Stream_iobuf__alloc (); + CORBA_sequence_set_release (*buffer, TRUE); + + (*buffer)->_buffer = + CORBA_sequence_CORBA_octet_allocbuf (realcount); + (*buffer)->_length = realcount; + + g_memmove ((*buffer)->_buffer, gbuf, realcount); + } + + g_free (gbuf); + } + + return realcount; +} + +static void +impl_content_stream_close (PortableServer_Servant servant, + CORBA_Environment * ev) +{ + GIOStatus status; + GError *err; + SpiContentStream *stream = + SPI_CONTENT_STREAM (bonobo_object_from_servant (servant)); + if (stream && stream->gio) + { + status = g_io_channel_shutdown (stream->gio, TRUE, &err); + g_io_channel_unref (stream->gio); + } + if (err) + g_free (err); +} + +static void +spi_content_stream_class_init (SpiContentStreamClass * klass) +{ + POA_Accessibility_ContentStream__epv *epv = &klass->epv; + GObjectClass *object_class = (GObjectClass *) klass; + + epv->seek = impl_content_stream_seek; + epv->read = impl_content_stream_read; + epv->close = impl_content_stream_close; + + object_class->dispose = spi_content_stream_dispose; +} + + +static void +spi_content_stream_init (SpiContentStream * stream) +{ +} + + +BONOBO_TYPE_FUNC_FULL (SpiContentStream, + Accessibility_ContentStream, + BONOBO_TYPE_OBJECT, spi_content_stream) + static AtkStreamableContent + *get_streamable_from_servant (PortableServer_Servant servant) +{ + SpiBase *object = SPI_BASE (bonobo_object_from_servant (servant)); + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (ATK_IS_STREAMABLE_CONTENT (object->gobj), NULL); + return ATK_STREAMABLE_CONTENT (object->gobj); +} + +/* + * CORBA Accessibility::StreamableContent::getContentTypes method implementation + */ +static Accessibility_StringSeq * +impl_accessibility_streamable_get_content_types (PortableServer_Servant + servant, + CORBA_Environment * ev) +{ + Accessibility_StringSeq *typelist = Accessibility_StringSeq__alloc (); + AtkStreamableContent *streamable = get_streamable_from_servant (servant); + int n_types, i; + + typelist->_length = typelist->_maximum = 0; + + g_return_val_if_fail (streamable != NULL, typelist); + + n_types = atk_streamable_content_get_n_mime_types (streamable); + + if (n_types) + { + typelist->_length = typelist->_maximum = n_types; + typelist->_buffer = Accessibility_StringSeq_allocbuf (n_types); + for (i = 0; i < n_types; ++i) + { + const gchar *mimetype = + atk_streamable_content_get_mime_type (streamable, i); + typelist->_buffer[i] = CORBA_string_dup (mimetype ? mimetype : ""); + } + } + return typelist; +} + +/* + * CORBA Accessibility::StreamableContent::getContent method implementation + */ +static Bonobo_Stream +impl_accessibility_streamable_get_content (PortableServer_Servant servant, + const CORBA_char * content_type, + CORBA_Environment * ev) +{ + Bonobo_Stream stream; + AtkStreamableContent *streamable = get_streamable_from_servant (servant); + GIOChannel *gio; + + g_return_val_if_fail (streamable != NULL, NULL); + + gio = atk_streamable_content_get_stream (streamable, content_type); + + stream = CORBA_OBJECT_NIL; /* deprecated, + * and it was never implemented, + * so don't bother fixing this + */ + return stream; +} + +/* + * CORBA Accessibility::StreamableContent::getStream method implementation + */ +static Accessibility_ContentStream +impl_accessibility_streamable_get_stream (PortableServer_Servant servant, + const CORBA_char * content_type, + CORBA_Environment * ev) +{ + SpiContentStream *stream; + AtkStreamableContent *streamable = get_streamable_from_servant (servant); + GIOChannel *gio; + + g_return_val_if_fail (streamable != NULL, NULL); + + gio = atk_streamable_content_get_stream (streamable, content_type); + + stream = spi_content_stream_new (gio); + + return bonobo_object_dup_ref (BONOBO_OBJREF (stream), ev); +} + +/* + * CORBA Accessibility::StreamableContent::GetURI method implementation + */ +static CORBA_string +impl_accessibility_streamable_get_uri (PortableServer_Servant servant, + const CORBA_char * content_type, + CORBA_Environment * ev) +{ + gchar *uri; + AtkStreamableContent *streamable = get_streamable_from_servant (servant); + + g_return_val_if_fail (streamable != NULL, NULL); + + uri = atk_streamable_content_get_uri (streamable, content_type); + + return (uri != NULL ? CORBA_string_dup (uri) : CORBA_string_dup ("")); +} + +static void +spi_streamable_class_init (SpiStreamableClass * klass) +{ + POA_Accessibility_StreamableContent__epv *epv = &klass->epv; + spi_streamable_parent_class = g_type_class_peek_parent (klass); + + epv->getContentTypes = impl_accessibility_streamable_get_content_types; + epv->getContent = impl_accessibility_streamable_get_content; + epv->getStream = impl_accessibility_streamable_get_stream; + epv->GetURI = impl_accessibility_streamable_get_uri; +} + +static void +spi_streamable_init (SpiStreamable * streamable) +{ +} + + +SpiStreamable * +spi_streamable_interface_new (AtkObject * o) +{ + SpiStreamable *retval = g_object_new (SPI_STREAMABLE_TYPE, NULL); + + spi_base_construct (SPI_BASE (retval), G_OBJECT (o)); + + return retval; +} + +BONOBO_TYPE_FUNC_FULL (SpiStreamable, + Accessibility_StreamableContent, + PARENT_TYPE, spi_streamable) diff --git a/atk-adaptor/adaptors/table-adaptor.c b/atk-adaptor/adaptors/table-adaptor.c new file mode 100644 index 0000000..d3bbba5 --- /dev/null +++ b/atk-adaptor/adaptors/table-adaptor.c @@ -0,0 +1,673 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_NRows (DBusMessageIter * iter, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + g_return_val_if_fail (ATK_IS_TABLE (user_data), FALSE); + return droute_return_v_int32 (iter, atk_table_get_n_rows (table)); +} + +static dbus_bool_t +impl_get_NColumns (DBusMessageIter * iter, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + g_return_val_if_fail (ATK_IS_TABLE (user_data), FALSE); + return droute_return_v_int32 (iter, atk_table_get_n_columns (table)); +} + +static dbus_bool_t +impl_get_Caption (DBusMessageIter * iter, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + g_return_val_if_fail (ATK_IS_TABLE (user_data), FALSE); + spi_object_append_v_reference (iter, atk_table_get_caption (table)); + return TRUE; +} + +static dbus_bool_t +impl_get_Summary (DBusMessageIter * iter, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + g_return_val_if_fail (ATK_IS_TABLE (user_data), FALSE); + spi_object_append_v_reference (iter, atk_table_get_summary (table)); + return TRUE; +} + +static dbus_bool_t +impl_get_NSelectedRows (DBusMessageIter * iter, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + gint *selected_rows = NULL; + int count; + g_return_val_if_fail (ATK_IS_TABLE (user_data), FALSE); + count = atk_table_get_selected_rows (table, &selected_rows); + if (selected_rows) + g_free (selected_rows); + return droute_return_v_int32 (iter, count); +} + +static dbus_bool_t +impl_get_NSelectedColumns (DBusMessageIter * iter, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + gint *selected_columns = NULL; + int count; + g_return_val_if_fail (ATK_IS_TABLE (user_data), FALSE); + count = atk_table_get_selected_columns (table, &selected_columns); + if (selected_columns) + g_free (selected_columns); + return droute_return_v_int32 (iter, count); +} + +static DBusMessage * +impl_GetAccessibleAt (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row, column; + DBusMessage *reply; + AtkObject *obj; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INT32, &column, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + obj = atk_table_ref_at (table, row, column); + reply = spi_object_return_reference (message, obj); + if (obj) + g_object_unref (obj); + + return reply; +} + +static DBusMessage * +impl_GetIndexAt (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row, column; + dbus_int32_t index; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INT32, &column, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + index = atk_table_get_index_at (table, row, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &index, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetRowAtIndex (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t index; + dbus_int32_t row; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + row = atk_table_get_row_at_index (table, index); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &row, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetColumnAtIndex (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t index; + dbus_int32_t column; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + column = atk_table_get_column_at_index (table, index); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &column, + DBUS_TYPE_INVALID); + } + return reply; +} + +static const gchar * +validate_unallocated_string (const gchar *str) +{ + if (!str) + return ""; + if (!g_utf8_validate (str, -1, NULL)) + { + g_warning ("atk-bridge: received bad UTF-8 string from a table function"); + return ""; + } + return str; +} + +static DBusMessage * +impl_GetRowDescription (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + dbus_int32_t row; + AtkTable *table = (AtkTable *) user_data; + const gchar *description; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + description = atk_table_get_row_description (table, row); + description = validate_unallocated_string (description); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &description, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetColumnDescription (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t column; + const char *description; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &column, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + description = atk_table_get_column_description (table, column); + description = validate_unallocated_string (description); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &description, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetRowExtentAt (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row, column; + dbus_int32_t extent; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INT32, &column, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + extent = atk_table_get_row_extent_at (table, row, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &extent, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetColumnExtentAt (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row, column; + dbus_int32_t extent; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INT32, &column, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + extent = atk_table_get_column_extent_at (table, row, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &extent, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetRowHeader (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row; + AtkObject *obj = NULL; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + obj = atk_table_get_row_header (table, row); + return spi_object_return_reference (message, obj); +} + +static DBusMessage * +impl_GetColumnHeader (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t column; + AtkObject *obj; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &column, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + obj = atk_table_get_column_header (table, column); + return spi_object_return_reference (message, obj); +} + +static DBusMessage * +impl_GetSelectedRows (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + gint *selected_rows = NULL; + gint count; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + count = atk_table_get_selected_rows (table, &selected_rows); + if (!selected_rows) + count = 0; + reply = dbus_message_new_method_return (message); + if (reply) + { + /* tbd - figure out if this is safe for a 0-length array */ + dbus_message_append_args (reply, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, + &selected_rows, count, DBUS_TYPE_INVALID); + } + if (selected_rows) + g_free (selected_rows); + return reply; +} + +static DBusMessage * +impl_GetSelectedColumns (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + gint *selected_columns = NULL; + gint count; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + count = atk_table_get_selected_columns (table, &selected_columns); + if (!selected_columns) + count = 0; + reply = dbus_message_new_method_return (message); + if (reply) + { + /* tbd - figure out if this is safe for a 0-length array */ + dbus_message_append_args (reply, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, + &selected_columns, count, DBUS_TYPE_INVALID); + } + if (selected_columns) + g_free (selected_columns); + return reply; +} + +static DBusMessage * +impl_IsRowSelected (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_is_row_selected (table, row); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_IsColumnSelected (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t column; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &column, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_is_column_selected (table, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_IsSelected (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row, column; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INT32, &column, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_is_selected (table, row, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_AddRowSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_add_row_selection (table, row); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_AddColumnSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t column; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &column, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_add_column_selection (table, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_RemoveRowSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t row; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &row, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_remove_row_selection (table, row); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_RemoveColumnSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t column; + DBusMessage *reply; + dbus_bool_t ret; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &column, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ret = atk_table_remove_column_selection (table, column); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetRowColumnExtentsAtIndex (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTable *table = (AtkTable *) user_data; + dbus_int32_t index; + dbus_int32_t row, column, row_extents, col_extents; + dbus_bool_t is_selected; + dbus_bool_t ret; + DBusMessage *reply; + AtkObject *cell; + AtkRole role = ATK_ROLE_INVALID; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + column = atk_table_get_column_at_index (table, index); + row = atk_table_get_row_at_index (table, index); + row_extents = atk_table_get_row_extent_at (table, row, column); + col_extents = atk_table_get_column_extent_at (table, row, column); + is_selected = atk_table_is_selected (table, row, column); + cell = atk_table_ref_at (table, row, column); + if (cell) + { + role = atk_object_get_role (cell); + g_object_unref (cell); + } + ret = (role == ATK_ROLE_TABLE_CELL ? TRUE : FALSE); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INT32, &row, DBUS_TYPE_INT32, + &column, DBUS_TYPE_INT32, &row_extents, + DBUS_TYPE_INT32, &col_extents, + DBUS_TYPE_BOOLEAN, &is_selected, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetAccessibleAt, "GetAccessibleAt"}, + {impl_GetIndexAt, "GetIndexAt"}, + {impl_GetRowAtIndex, "GetRowAtIndex"}, + {impl_GetColumnAtIndex, "GetColumnAtIndex"}, + {impl_GetRowDescription, "GetRowDescription"}, + {impl_GetColumnDescription, "GetColumnDescription"}, + {impl_GetRowExtentAt, "GetRowExtentAt"}, + {impl_GetColumnExtentAt, "GetColumnExtentAt"}, + {impl_GetRowHeader, "GetRowHeader"}, + {impl_GetColumnHeader, "GetColumnHeader"}, + {impl_GetSelectedRows, "GetSelectedRows"}, + {impl_GetSelectedColumns, "GetSelectedColumns"}, + {impl_IsRowSelected, "IsRowSelected"}, + {impl_IsColumnSelected, "IsColumnSelected"}, + {impl_IsSelected, "IsSelected"}, + {impl_AddRowSelection, "AddRowSelection"}, + {impl_AddColumnSelection, "AddColumnSelection"}, + {impl_RemoveRowSelection, "RemoveRowSelection"}, + {impl_RemoveColumnSelection, "RemoveColumnSelection"}, + {impl_GetRowColumnExtentsAtIndex, "GetRowColumnExtentsAtIndex"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_NRows, NULL, "NRows"}, + {impl_get_NColumns, NULL, "NColumns"}, + {impl_get_Caption, NULL, "Caption"}, + {impl_get_Summary, NULL, "Summary"}, + {impl_get_NSelectedRows, NULL, "NSelectedRows"}, + {impl_get_NSelectedColumns, NULL, "NSelectedColumns"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_table (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_TABLE, spi_org_a11y_atspi_Table, methods, properties); +}; diff --git a/atk-adaptor/adaptors/table-cell-adaptor.c b/atk-adaptor/adaptors/table-cell-adaptor.c new file mode 100644 index 0000000..c557ed5 --- /dev/null +++ b/atk-adaptor/adaptors/table-cell-adaptor.c @@ -0,0 +1,191 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2013 SUSE LLC. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <atk/atk.h> +#include <droute/droute.h> + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_ColumnSpan (DBusMessageIter * iter, void *user_data) +{ + AtkTableCell *cell = (AtkTableCell *) user_data; + g_return_val_if_fail (ATK_IS_TABLE_CELL (user_data), FALSE); + return droute_return_v_int32 (iter, atk_table_cell_get_column_span (cell)); +} + +static DBusMessage * +message_from_object_array (DBusMessage *message, GPtrArray *array) +{ + DBusMessage *reply; + DBusMessageIter iter, iter_array; + gint len; + gint i; + + reply = dbus_message_new_method_return (message); + if (!reply) + return NULL; + + dbus_message_iter_init_append (reply, &iter); + + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array)) + return reply; /* TODO: handle out of memory */ + len = (array? array->len: 0); + for (i = 0; i < len; i++) + { + spi_object_append_reference (&iter_array, g_ptr_array_index (array, i)); + } + dbus_message_iter_close_container (&iter, &iter_array); + g_ptr_array_unref (array); + return message; +} + +static DBusMessage * +impl_GetColumnHeaderCells (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTableCell *cell = user_data; + GPtrArray *array; + + g_return_val_if_fail (ATK_IS_TABLE_CELL (user_data), + droute_not_yet_handled_error (message)); + + array = atk_table_cell_get_column_header_cells (cell); + return message_from_object_array (message, array); +} + +static dbus_bool_t +impl_get_RowSpan (DBusMessageIter * iter, void *user_data) +{ + AtkTableCell *cell = (AtkTableCell *) user_data; + g_return_val_if_fail (ATK_IS_TABLE_CELL (user_data), FALSE); + return droute_return_v_int32 (iter, atk_table_cell_get_row_span (cell)); +} + +static DBusMessage * +impl_GetRowHeaderCells (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTableCell *cell = user_data; + GPtrArray *array; + + g_return_val_if_fail (ATK_IS_TABLE_CELL (user_data), + droute_not_yet_handled_error (message)); + + array = atk_table_cell_get_row_header_cells (cell); + return message_from_object_array (message, array); +} + +static dbus_bool_t +impl_get_Position (DBusMessageIter * iter, void *user_data) +{ + AtkTableCell *cell = (AtkTableCell *) user_data; + gint row = -1, column = -1; + dbus_int32_t d_row, d_column; + DBusMessageIter iter_struct, iter_variant; + + g_return_val_if_fail (ATK_IS_TABLE_CELL (user_data), FALSE); + if (!atk_table_cell_get_position (cell, &row, &column)) + return FALSE; + + d_row = row; + d_column = column; + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, "(ii)", &iter_variant); + dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_STRUCT, NULL, &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &row); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &column); + dbus_message_iter_close_container (&iter_variant, &iter_struct); + dbus_message_iter_close_container (iter, &iter_variant); + return TRUE; +} + +static dbus_bool_t +impl_get_Table (DBusMessageIter * iter, void *user_data) +{ + AtkTableCell *cell = (AtkTableCell *) user_data; + AtkObject *table; + + g_return_val_if_fail (ATK_IS_TABLE_CELL (user_data), FALSE); + + table = atk_table_cell_get_table (cell); + if (!table) + return FALSE; + spi_object_append_reference (iter, table); + return TRUE; +} + +static DBusMessage * +impl_GetRowColumnSpan (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkTableCell *cell = (AtkTableCell *) user_data; + gint row, column, row_span, column_span; + dbus_int32_t d_row, d_column, d_row_span, d_column_span; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TABLE (user_data), + droute_not_yet_handled_error (message)); + atk_table_cell_get_row_column_span (cell, &row, &column, &row_span, + &column_span); + d_row = row; + d_column = column; + d_row_span = row_span; + d_column_span = column_span; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &d_row, DBUS_TYPE_INT32, + &d_column, DBUS_TYPE_INT32, &d_row_span, + DBUS_TYPE_INT32, &d_column_span, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetRowHeaderCells, "GetRowHeaderCells"}, + {impl_GetColumnHeaderCells, "GetColumnHeaderCells"}, + {impl_GetRowColumnSpan, "GetRowColumnSpan"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_ColumnSpan, NULL, "ColumnSpan"}, + {impl_get_Position, NULL, "Position"}, + {impl_get_RowSpan, NULL, "RowSpan"}, + {impl_get_Table, NULL, "Table"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_table_cell (DRoutePath * path) +{ + droute_path_add_interface (path, + ATSPI_DBUS_INTERFACE_TABLE_CELL, + spi_org_a11y_atspi_TableCell, + methods, properties); +}; diff --git a/atk-adaptor/adaptors/text-adaptor.c b/atk-adaptor/adaptors/text-adaptor.c new file mode 100644 index 0000000..be10349 --- /dev/null +++ b/atk-adaptor/adaptors/text-adaptor.c @@ -0,0 +1,892 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "object.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_CharacterCount (DBusMessageIter * iter, void *user_data) +{ + AtkText *text = (AtkText *) user_data; + g_return_val_if_fail (ATK_IS_TEXT (user_data), FALSE); + return droute_return_v_int32 (iter, atk_text_get_character_count (text)); +} + +static dbus_bool_t +impl_get_CaretOffset (DBusMessageIter * iter, void *user_data) +{ + AtkText *text = (AtkText *) user_data; + g_return_val_if_fail (ATK_IS_TEXT (user_data), FALSE); + return droute_return_v_int32 (iter, atk_text_get_caret_offset (text)); +} + +static gchar * +validate_allocated_string (gchar *str) +{ + if (!str) + return g_strdup (""); + if (!g_utf8_validate (str, -1, NULL)) + { + g_warning ("atk-bridge: received bad UTF-8 string from a get_text function"); + g_free (str); + return g_strdup (""); + } + return str; +} + +static DBusMessage * +impl_GetText (DBusConnection * bus, DBusMessage * message, void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t startOffset, endOffset; + gchar *txt; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32, + &endOffset, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + txt = atk_text_get_text (text, startOffset, endOffset); + txt = validate_allocated_string (txt); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt, + DBUS_TYPE_INVALID); + } + g_free (txt); + return reply; +} + +static DBusMessage * +impl_SetCaretOffset (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_text_set_caret_offset (text, offset); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetTextBeforeOffset (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_uint32_t type; + gchar *txt; + dbus_int32_t startOffset, endOffset; + gint intstart_offset = 0, intend_offset = 0; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + txt = + atk_text_get_text_before_offset (text, offset, (AtkTextBoundary) type, + &intstart_offset, &intend_offset); + startOffset = intstart_offset; + endOffset = intend_offset; + txt = validate_allocated_string (txt); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt, + DBUS_TYPE_INT32, &startOffset, + DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_INVALID); + } + g_free (txt); + return reply; +} + +static DBusMessage * +impl_GetTextAtOffset (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset, type; + gchar *txt; + dbus_int32_t startOffset, endOffset; + gint intstart_offset = 0, intend_offset = 0; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + txt = + atk_text_get_text_at_offset (text, offset, (AtkTextBoundary) type, + &intstart_offset, &intend_offset); + startOffset = intstart_offset; + endOffset = intend_offset; + txt = validate_allocated_string (txt); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt, + DBUS_TYPE_INT32, &startOffset, + DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_INVALID); + } + g_free (txt); + return reply; +} + +static DBusMessage * +impl_GetTextAfterOffset (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_uint32_t type; + gchar *txt; + dbus_int32_t startOffset, endOffset; + gint intstart_offset = 0, intend_offset = 0; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &type, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + txt = + atk_text_get_text_after_offset (text, offset, (AtkTextBoundary) type, + &intstart_offset, &intend_offset); + startOffset = intstart_offset; + endOffset = intend_offset; + txt = validate_allocated_string (txt); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt, + DBUS_TYPE_INT32, &startOffset, + DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_INVALID); + } + g_free (txt); + return reply; +} + +static DBusMessage * +impl_GetCharacterAtOffset (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_int32_t ch; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + ch = atk_text_get_character_at_offset (text, offset); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &ch, + DBUS_TYPE_INVALID); + } + return reply; +} + +static gchar * +get_text_for_legacy_implementations(AtkText *text, + gint offset, + AtkTextGranularity granularity, + gint *start_offset, + gint *end_offset) +{ + gchar *txt = 0; + AtkTextBoundary boundary = 0; + switch (granularity) { + case ATK_TEXT_GRANULARITY_CHAR: + boundary = ATK_TEXT_BOUNDARY_CHAR; + break; + + case ATK_TEXT_GRANULARITY_WORD: + boundary = ATK_TEXT_BOUNDARY_WORD_START; + break; + + case ATK_TEXT_GRANULARITY_SENTENCE: + boundary = ATK_TEXT_BOUNDARY_SENTENCE_START; + break; + + case ATK_TEXT_GRANULARITY_LINE: + boundary = ATK_TEXT_BOUNDARY_LINE_START; + break; + + case ATK_TEXT_GRANULARITY_PARAGRAPH: + /* This is not implemented in previous versions of ATK */ + txt = g_strdup(""); + break; + + default: + g_assert_not_reached(); + } + + if (!txt) + { + txt = + atk_text_get_text_at_offset (text, offset, boundary, + start_offset, end_offset); + } + + return txt; +} + +static DBusMessage * +impl_GetStringAtOffset (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_uint32_t granularity; + gchar *txt = 0; + dbus_int32_t startOffset, endOffset; + gint intstart_offset = 0, intend_offset = 0; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &granularity, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + txt = + atk_text_get_string_at_offset (text, offset, (AtkTextGranularity) granularity, + &intstart_offset, &intend_offset); + + /* Accessibility layers implementing an older version of ATK (even if + * a new enough version of libatk is installed) might return NULL due + * not to provide an implementation for get_string_at_offset(), so we + * try with the legacy implementation if that's the case. */ + if (!txt) + txt = get_text_for_legacy_implementations(text, offset, + (AtkTextGranularity) granularity, + &intstart_offset, &intend_offset); + + startOffset = intstart_offset; + endOffset = intend_offset; + txt = validate_allocated_string (txt); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &txt, + DBUS_TYPE_INT32, &startOffset, + DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_INVALID); + } + g_free (txt); + return reply; +} + +static DBusMessage * +impl_GetAttributeValue (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + char *attributeName; + dbus_int32_t startOffset, endOffset; + dbus_bool_t defined; + gint intstart_offset = 0, intend_offset = 0; + char *rv = NULL; + DBusMessage *reply; + AtkAttributeSet *set; + GSList *cur_attr; + AtkAttribute *at; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_STRING, + &attributeName, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + set = atk_text_get_run_attributes (text, offset, + &intstart_offset, &intend_offset); + startOffset = intstart_offset; + endOffset = intend_offset; + defined = FALSE; + cur_attr = (GSList *) set; + while (cur_attr) + { + at = (AtkAttribute *) cur_attr->data; + if (!strcmp (at->name, attributeName)) + { + rv = at->value; + defined = TRUE; + break; + } + cur_attr = cur_attr->next; + } + if (!rv) + rv = ""; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_STRING, &rv, DBUS_TYPE_INT32, + &startOffset, DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_BOOLEAN, &defined, + DBUS_TYPE_INVALID); + } + atk_attribute_set_free (set); + return reply; +} + +static DBusMessage * +impl_GetAttributes (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_int32_t startOffset, endOffset; + gint intstart_offset, intend_offset; + DBusMessage *reply; + AtkAttributeSet *set; + DBusMessageIter iter; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + set = atk_text_get_run_attributes (text, offset, + &intstart_offset, &intend_offset); + + startOffset = intstart_offset; + endOffset = intend_offset; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_iter_init_append (reply, &iter); + spi_object_append_attribute_set (&iter, set); + dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset, + DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_INVALID); + } + atk_attribute_set_free (set); + return reply; +} + +static DBusMessage * +impl_GetDefaultAttributes (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + DBusMessage *reply; + AtkAttributeSet *set; + DBusMessageIter iter; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + + set = atk_text_get_default_attributes (text); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_iter_init_append (reply, &iter); + spi_object_append_attribute_set (&iter, set); + } + atk_attribute_set_free (set); + return reply; +} + +static DBusMessage * +impl_GetCharacterExtents (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_uint32_t coordType; + dbus_int32_t x, y, width, height; + gint ix = 0, iy = 0, iw = 0, ih = 0; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, + &coordType, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + atk_text_get_character_extents (text, offset, &ix, &iy, &iw, &ih, + (AtkCoordType) coordType); + x = ix; + y = iy; + width = iw; + height = ih; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, + &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32, + &height, DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetOffsetAtPoint (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t x, y; + dbus_uint32_t coordType; + dbus_int32_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, + DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_text_get_offset_at_point (text, x, y, coordType); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetNSelections (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + rv = atk_text_get_n_selections (text); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t selectionNum; + dbus_int32_t startOffset, endOffset; + gint intstart_offset = 0, intend_offset = 0; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + /* atk_text_get_selection returns gchar * which we discard */ + g_free (atk_text_get_selection + (text, selectionNum, &intstart_offset, &intend_offset)); + startOffset = intstart_offset; + endOffset = intend_offset; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &startOffset, + DBUS_TYPE_INT32, &endOffset, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_AddSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t startOffset, endOffset; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32, + &endOffset, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_text_add_selection (text, startOffset, endOffset); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_RemoveSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t selectionNum; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_text_remove_selection (text, selectionNum); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_SetSelection (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t selectionNum, startOffset, endOffset; + dbus_bool_t rv; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &selectionNum, DBUS_TYPE_INT32, + &startOffset, DBUS_TYPE_INT32, &endOffset, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rv = atk_text_set_selection (text, selectionNum, startOffset, endOffset); + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DBusMessage * +impl_GetRangeExtents (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t startOffset, endOffset; + dbus_uint32_t coordType; + AtkTextRectangle rect; + dbus_int32_t x, y, width, height; + DBusMessage *reply; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &startOffset, DBUS_TYPE_INT32, + &endOffset, DBUS_TYPE_UINT32, &coordType, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + memset (&rect, 0, sizeof (rect)); + atk_text_get_range_extents (text, startOffset, endOffset, + (AtkCoordType) coordType, &rect); + x = rect.x; + y = rect.y; + width = rect.width; + height = rect.height; + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, + &y, DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32, + &height, DBUS_TYPE_INVALID); + } + return reply; +} + +#define MAXRANGELEN 512 + +static DBusMessage * +impl_GetBoundedRanges (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t x, y, width, height; + dbus_uint32_t coordType, xClipType, yClipType; + AtkTextRange **range_list = NULL; + AtkTextRectangle rect; + DBusMessage *reply; + DBusMessageIter iter, array, struc, variant; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, + DBUS_TYPE_INT32, &height, DBUS_TYPE_INT32, &width, DBUS_TYPE_UINT32, + &coordType, DBUS_TYPE_UINT32, &xClipType, DBUS_TYPE_UINT32, &yClipType, + DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + range_list = + atk_text_get_bounded_ranges (text, &rect, (AtkCoordType) coordType, + (AtkTextClipType) xClipType, + (AtkTextClipType) yClipType); + reply = dbus_message_new_method_return (message); + if (!reply) + return NULL; + /* This isn't pleasant. */ + dbus_message_iter_init_append (reply, &iter); + if (dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "(iisv)", &array)) + { + int len; + int count = (range_list ? MAXRANGELEN : 0); + for (len = 0; len < count && range_list[len]; ++len) + { + if (dbus_message_iter_open_container + (&array, DBUS_TYPE_STRUCT, NULL, &struc)) + { + dbus_int32_t val; + val = range_list[len]->start_offset; + dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val); + val = range_list[len]->end_offset; + dbus_message_iter_append_basic (&struc, DBUS_TYPE_INT32, &val); + dbus_message_iter_append_basic (&struc, DBUS_TYPE_STRING, + &range_list[len]->content); + /* The variant is unimplemented in atk, but I don't want to + * unilaterally muck with the spec and remove it, so I'll just + * throw in a dummy value */ + if (dbus_message_iter_open_container + (&struc, DBUS_TYPE_VARIANT, "i", &variant)) + { + dbus_uint32_t dummy = 0; + dbus_message_iter_append_basic (&variant, DBUS_TYPE_INT32, + &dummy); + dbus_message_iter_close_container (&struc, &variant); + } + dbus_message_iter_close_container (&array, &struc); + g_free (range_list[len]->content); + g_free (range_list[len]); + } + } + dbus_message_iter_close_container (&iter, &array); + } + + if (range_list) + g_free (range_list); + + return reply; +} + +static DBusMessage * +impl_GetAttributeRun (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + dbus_int32_t offset; + dbus_bool_t includeDefaults; + dbus_int32_t startOffset, endOffset; + gint intstart_offset = 0, intend_offset = 0; + DBusMessage *reply; + AtkAttributeSet *attributes = NULL; + DBusMessageIter iter; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_INT32, &offset, DBUS_TYPE_BOOLEAN, + &includeDefaults, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + attributes = + atk_text_get_run_attributes (text, offset, &intstart_offset, + &intend_offset); + + if (includeDefaults) + { + attributes = g_slist_concat (attributes, + atk_text_get_default_attributes (text)); + } + + reply = dbus_message_new_method_return (message); + if (!reply) + return NULL; + + dbus_message_iter_init_append (reply, &iter); + spi_object_append_attribute_set (&iter, attributes); + + startOffset = intstart_offset; + endOffset = intend_offset; + dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &startOffset); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &endOffset); + + atk_attribute_set_free (attributes); + + return reply; +} + +static DBusMessage * +impl_GetDefaultAttributeSet (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkText *text = (AtkText *) user_data; + DBusMessage *reply; + DBusMessageIter iter; + AtkAttributeSet *attributes; + + g_return_val_if_fail (ATK_IS_TEXT (user_data), + droute_not_yet_handled_error (message)); + + attributes = atk_text_get_default_attributes (text); + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_iter_init_append (reply, &iter); + spi_object_append_attribute_set (&iter, attributes); + } + + if (attributes) + atk_attribute_set_free (attributes); + + return reply; +} + +static DRouteMethod methods[] = { + {impl_GetText, "GetText"}, + {impl_SetCaretOffset, "SetCaretOffset"}, + {impl_GetTextBeforeOffset, "GetTextBeforeOffset"}, + {impl_GetTextAtOffset, "GetTextAtOffset"}, + {impl_GetTextAfterOffset, "GetTextAfterOffset"}, + {impl_GetStringAtOffset, "GetStringAtOffset"}, + {impl_GetCharacterAtOffset, "GetCharacterAtOffset"}, + {impl_GetAttributeValue, "GetAttributeValue"}, + {impl_GetAttributes, "GetAttributes"}, + {impl_GetDefaultAttributes, "GetDefaultAttributes"}, + {impl_GetCharacterExtents, "GetCharacterExtents"}, + {impl_GetOffsetAtPoint, "GetOffsetAtPoint"}, + {impl_GetNSelections, "GetNSelections"}, + {impl_GetSelection, "GetSelection"}, + {impl_AddSelection, "AddSelection"}, + {impl_RemoveSelection, "RemoveSelection"}, + {impl_SetSelection, "SetSelection"}, + {impl_GetRangeExtents, "GetRangeExtents"}, + {impl_GetBoundedRanges, "GetBoundedRanges"}, + {impl_GetAttributeRun, "GetAttributeRun"}, + {impl_GetDefaultAttributeSet, "GetDefaultAttributeSet"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_CharacterCount, NULL, "CharacterCount"}, + {impl_get_CaretOffset, NULL, "CaretOffset"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_text (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_TEXT, spi_org_a11y_atspi_Text, methods, properties); +}; diff --git a/atk-adaptor/adaptors/value-adaptor.c b/atk-adaptor/adaptors/value-adaptor.c new file mode 100644 index 0000000..9767fc4 --- /dev/null +++ b/atk-adaptor/adaptors/value-adaptor.c @@ -0,0 +1,208 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <math.h> + +#include <atk/atk.h> +#include <droute/droute.h> +#include "bridge.h" + +#include "spi-dbus.h" +#include "introspection.h" + +static dbus_bool_t +impl_get_MinimumValue (DBusMessageIter * iter, void *user_data) +{ + AtkValue *value = (AtkValue *) user_data; + GValue src = { 0 }; + GValue dest = { 0 }; + gdouble dub; + + g_return_val_if_fail (ATK_IS_VALUE (user_data), FALSE); + + g_value_init (&src, G_TYPE_DOUBLE); + atk_value_get_minimum_value (value, &src); + g_value_init (&dest, G_TYPE_DOUBLE); + + if (g_value_transform (&src, &dest)) + { + dub = g_value_get_double (&dest); + return droute_return_v_double (iter, dub); + } + else + { + return FALSE; + } +} + +static dbus_bool_t +impl_get_MaximumValue (DBusMessageIter * iter, void *user_data) +{ + AtkValue *value = (AtkValue *) user_data; + GValue src = { 0 }; + GValue dest = { 0 }; + gdouble dub = 0; + + g_return_val_if_fail (ATK_IS_VALUE (user_data), FALSE); + + g_value_init (&src, G_TYPE_DOUBLE); + atk_value_get_maximum_value (value, &src); + g_value_init (&dest, G_TYPE_DOUBLE); + + if (g_value_transform (&src, &dest)) + { + dub = g_value_get_double (&dest); + } + return droute_return_v_double (iter, dub); +} + +static dbus_bool_t +impl_get_MinimumIncrement (DBusMessageIter * iter, void *user_data) +{ + AtkValue *value = (AtkValue *) user_data; + GValue src = { 0 }; + GValue dest = { 0 }; + gdouble dub = 0; + + g_return_val_if_fail (ATK_IS_VALUE (user_data), FALSE); + + g_value_init (&src, G_TYPE_DOUBLE); + atk_value_get_minimum_increment (value, &src); + g_value_init (&dest, G_TYPE_DOUBLE); + + if (g_value_transform (&src, &dest)) + { + dub = g_value_get_double (&dest); + } + return droute_return_v_double (iter, dub); +} + +static dbus_bool_t +impl_get_CurrentValue (DBusMessageIter * iter, void *user_data) +{ + AtkValue *value = (AtkValue *) user_data; + GValue src = { 0 }; + GValue dest = { 0 }; + gdouble dub = 0; + + g_return_val_if_fail (ATK_IS_VALUE (user_data), FALSE); + + g_value_init (&src, G_TYPE_DOUBLE); + atk_value_get_current_value (value, &src); + g_value_init (&dest, G_TYPE_DOUBLE); + + if (g_value_transform (&src, &dest)) + { + dub = g_value_get_double (&dest); + } + return droute_return_v_double (iter, dub); +} + +static dbus_bool_t +impl_set_CurrentValue (DBusMessageIter * iter, void *user_data) +{ + AtkValue *value = (AtkValue *) user_data; + GValue src = { 0 }; + GValue dest = { 0 }; + gdouble dub; + DBusMessageIter iter_variant; + + g_return_val_if_fail (ATK_IS_VALUE (user_data), FALSE); + + dbus_message_iter_recurse (iter, &iter_variant); + if (dbus_message_iter_get_arg_type (&iter_variant) != DBUS_TYPE_DOUBLE) + { + g_warning ("TODO: Support setting value from a non-double"); + return FALSE; + } + dbus_message_iter_get_basic (&iter_variant, &dub); + g_value_init (&src, G_TYPE_DOUBLE); + g_value_set_double (&src, dub); + + atk_value_get_current_value (value, &dest); + + if (g_value_transform (&src, &dest)) + { + atk_value_set_current_value (value, &dest); + return TRUE; + } + else + { + return FALSE; + } +} + +/* keeping this method around for backwards-compatibility for now; see + * * BGO#652596 */ +static DBusMessage * +impl_SetCurrentValue (DBusConnection * bus, DBusMessage * message, + void *user_data) +{ + AtkValue *value = (AtkValue *) user_data; + dbus_bool_t rv; + DBusMessage *reply; + gdouble dub = 0; + GValue new_value = { 0 }; + + g_return_val_if_fail (ATK_IS_VALUE (value), + droute_not_yet_handled_error (message)); + + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_DOUBLE, &dub, DBUS_TYPE_INVALID)) + { + return droute_invalid_arguments_error (message); + } + + g_value_init (&new_value, G_TYPE_DOUBLE); + g_value_set_double (&new_value, dub); + rv = atk_value_set_current_value (value, &new_value); + + reply = dbus_message_new_method_return (message); + if (reply) + { + dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &rv, + DBUS_TYPE_INVALID); + } + return reply; +} + +static DRouteMethod methods[] = { + {impl_SetCurrentValue, "SetCurrentValue"}, + {NULL, NULL} +}; + +static DRouteProperty properties[] = { + {impl_get_MinimumValue, NULL, "MinimumValue"}, + {impl_get_MaximumValue, NULL, "MaximumValue"}, + {impl_get_MinimumIncrement, NULL, "MinimumIncrement"}, + {impl_get_CurrentValue, impl_set_CurrentValue, "CurrentValue"}, + {NULL, NULL, NULL} +}; + +void +spi_initialize_value (DRoutePath * path) +{ + spi_atk_add_interface (path, + ATSPI_DBUS_INTERFACE_VALUE, spi_org_a11y_atspi_Value, methods, properties); +}; diff --git a/atk-adaptor/atk-bridge.h b/atk-adaptor/atk-bridge.h new file mode 100644 index 0000000..9c0dd0d --- /dev/null +++ b/atk-adaptor/atk-bridge.h @@ -0,0 +1,37 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. + * Copyright 2008, 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ATK_BRIDGE_H +#define ATK_BRIDGE_H + +#include <glib.h> + +G_BEGIN_DECLS + +int atk_bridge_adaptor_init (int * argc, char ** argv[]); +void atk_bridge_adaptor_cleanup (void); + +G_END_DECLS + +#endif /* ATK_BRIDGE_H */ diff --git a/atk-adaptor/atkbridge.symbols b/atk-adaptor/atkbridge.symbols new file mode 100644 index 0000000..d90cf94 --- /dev/null +++ b/atk-adaptor/atkbridge.symbols @@ -0,0 +1,2 @@ +atk_bridge_adaptor_init +atk_bridge_adaptor_cleanup diff --git a/atk-adaptor/bitarray.h b/atk-adaptor/bitarray.h new file mode 100644 index 0000000..61ed673 --- /dev/null +++ b/atk-adaptor/bitarray.h @@ -0,0 +1,32 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _BITARRAY_H +#define _BITARRAY_H + +#include "dbus/dbus.h" +#include "glib.h" + +#define BITARRAY_SEQ_TERM 0xffffffff + +#define BITARRAY_SET(p, n) ((p)[n>>5] |= (1<<(n&31))) +#endif /* _BITARRAY_H */ diff --git a/atk-adaptor/bridge.c b/atk-adaptor/bridge.c new file mode 100644 index 0000000..e542094 --- /dev/null +++ b/atk-adaptor/bridge.c @@ -0,0 +1,1232 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008, 2009 Codethink Ltd. + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include "config.h" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include<sys/stat.h> +#include <atk/atk.h> + +#include <droute/droute.h> +#include <atspi/atspi.h> +#include <atk-bridge.h> + +#include "bridge.h" +#include "event.h" +#include "adaptors.h" +#include "object.h" +#include "accessible-stateset.h" + +#include "accessible-register.h" +#include "accessible-leasing.h" +#include "accessible-cache.h" + +#include "spi-dbus.h" + +/*---------------------------------------------------------------------------*/ + +static DBusHandlerResult +signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data); + +SpiBridge *spi_global_app_data = NULL; + +static gboolean inited = FALSE; + +/*---------------------------------------------------------------------------*/ + +static event_data * +add_event (const char *bus_name, const char *event) +{ + event_data *evdata; + gchar **data; + + spi_atk_add_client (bus_name); + evdata = g_new0 (event_data, 1); + data = g_strsplit (event, ":", 3); + if (!data) + { + g_free (evdata); + return NULL; + } + evdata->bus_name = g_strdup (bus_name); + evdata->data = data; + spi_global_app_data->events = g_list_append (spi_global_app_data->events, evdata); + return evdata; +} + +static GSList *clients = NULL; + +static void +tally_event_reply () +{ + static int replies_received = 0; + + if (!spi_global_app_data) + return; + + replies_received++; + if (replies_received == 3) + { + if (!clients) + spi_atk_deregister_event_listeners (); + spi_global_app_data->events_initialized = TRUE; + } +} + +GType +_atk_bridge_type_from_iface (const char *iface) +{ + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_ACCESSIBLE)) + return ATK_TYPE_OBJECT; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_ACTION)) + return ATK_TYPE_ACTION; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_COMPONENT)) + return ATK_TYPE_COMPONENT; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_DOCUMENT)) + return ATK_TYPE_DOCUMENT; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_HYPERTEXT)) + return ATK_TYPE_HYPERTEXT; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_HYPERLINK)) + return ATK_TYPE_HYPERLINK; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_IMAGE)) + return ATK_TYPE_IMAGE; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_SELECTION)) + return ATK_TYPE_SELECTION; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_TABLE)) + return ATK_TYPE_TABLE; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_TEXT)) + return ATK_TYPE_TEXT; + if (!strcmp (iface, ATSPI_DBUS_INTERFACE_VALUE)) + return ATK_TYPE_VALUE; + return 0; +} + +DRoutePropertyFunction +_atk_bridge_find_property_func (const char *property, GType *type) +{ + const char *iface; + const char *member; + DRouteProperty *dp; + + if (!strncasecmp (property, "action.", 7)) + { + iface = ATSPI_DBUS_INTERFACE_ACTION; + member = property + 7; + } + else if (!strncasecmp (property, "component.", 10)) + { + iface = ATSPI_DBUS_INTERFACE_COMPONENT; + member = property + 10; + } + else if (!strncasecmp (property, "selection.", 10)) + { + iface = ATSPI_DBUS_INTERFACE_SELECTION; + member = property + 10; + } + else if (!strncasecmp (property, "table.", 6)) + { + iface = ATSPI_DBUS_INTERFACE_TABLE; + member = property + 6; + } + else if (!strncasecmp (property, "text.", 5)) + { + iface = ATSPI_DBUS_INTERFACE_TEXT; + member = property + 5; + } + else if (!strncasecmp (property, "value.", 6)) + { + iface = ATSPI_DBUS_INTERFACE_VALUE; + member = property + 6; + } + else + { + iface = ATSPI_DBUS_INTERFACE_ACCESSIBLE; + member = property; + } + + *type = _atk_bridge_type_from_iface (iface); + + dp = g_hash_table_lookup (spi_global_app_data->property_hash, iface); + + if (!dp) + return NULL; + + for (;dp->name; dp++) + { + if (!strcasecmp (dp->name, member)) + { + return dp->get; + } + } + return NULL; +} + +static void +add_property_to_event (event_data *evdata, const char *property) +{ + AtspiPropertyDefinition *prop = g_new0 (AtspiPropertyDefinition, 1); + prop->func = _atk_bridge_find_property_func (property, &prop->type); + if (!prop->func) + { + g_warning ("atk-bridge: Request for unknown property '%s'", property); + g_free (prop); + return; + } + + prop->name = g_strdup (property); + + if (evdata) + { + evdata->properties = g_slist_append (evdata->properties, prop); + } +} + +static void +add_event_from_iter (DBusMessageIter *iter) +{ + const char *bus_name, *event; + event_data *evdata; + + dbus_message_iter_get_basic (iter, &bus_name); + dbus_message_iter_next (iter); + dbus_message_iter_get_basic (iter, &event); + dbus_message_iter_next (iter); + evdata = add_event (bus_name, event); + if (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY) + { + DBusMessageIter iter_sub_array; + dbus_message_iter_recurse (iter, &iter_sub_array); + while (dbus_message_iter_get_arg_type (&iter_sub_array) != DBUS_TYPE_INVALID) + { + const char *property; + dbus_message_iter_get_basic (&iter_sub_array, &property); + add_property_to_event (evdata, property); + dbus_message_iter_next (&iter_sub_array); + } + } +} + +static void +get_events_reply (DBusPendingCall *pending, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply (pending); + DBusMessageIter iter, iter_array, iter_struct; + + if (!reply || !spi_global_app_data) + goto done; + + if (strcmp (dbus_message_get_signature (reply), "a(ss)") != 0 && + strcmp (dbus_message_get_signature (reply), "a(ssas)") != 0) + { + g_warning ("atk-bridge: GetRegisteredEvents returned message with unknown signature"); + goto done; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + dbus_message_iter_recurse (&iter_array, &iter_struct); + add_event_from_iter (&iter_struct); + dbus_message_iter_next (&iter_array); + } + +done: + if (reply) + dbus_message_unref (reply); + if (pending) + dbus_pending_call_unref (pending); + + tally_event_reply (); +} + +static void +get_device_events_reply (DBusPendingCall *pending, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply (pending); + DBusMessageIter iter, iter_array, iter_struct; + + if (!reply) + goto done; + + if (strncmp (dbus_message_get_signature (reply), "a(s", 3) != 0) + { + g_warning ("atk-bridge: get_device_events_reply: unknown signature"); + goto done; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + char *bus_name; + dbus_message_iter_recurse (&iter_array, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &bus_name); + spi_atk_add_client (bus_name); + dbus_message_iter_next (&iter_array); + } + +done: + if (reply) + dbus_message_unref (reply); + if (pending) + dbus_pending_call_unref (pending); + + tally_event_reply (); +} + +static void +get_registered_event_listeners (SpiBridge *app) +{ + DBusMessage *message; + DBusPendingCall *pending = NULL; + + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_REGISTRY, + ATSPI_DBUS_INTERFACE_REGISTRY, + "GetRegisteredEvents"); + if (!message) + return; + + dbus_connection_send_with_reply (app->bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) + { + spi_global_app_data->events_initialized = TRUE; + return; + } + dbus_pending_call_set_notify (pending, get_events_reply, NULL, NULL); + + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_DEC, + ATSPI_DBUS_INTERFACE_DEC, + "GetKeystrokeListeners"); + if (!message) + return; + pending = NULL; + dbus_connection_send_with_reply (app->bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) + { + spi_global_app_data->events_initialized = TRUE; + return; + } + dbus_pending_call_set_notify (pending, get_device_events_reply, NULL, NULL); + + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_DEC, + ATSPI_DBUS_INTERFACE_DEC, + "GetDeviceEventListeners"); + if (!message) + return; + pending = NULL; + dbus_connection_send_with_reply (app->bus, message, &pending, -1); + dbus_message_unref (message); + if (!pending) + { + spi_global_app_data->events_initialized = TRUE; + return; + } + dbus_pending_call_set_notify (pending, get_device_events_reply, NULL, NULL); +} + +static void +register_reply (DBusPendingCall *pending, void *user_data) +{ + DBusMessage *reply; + SpiBridge *app = user_data; + + reply = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + + if (!spi_global_app_data) + { + if (reply) + dbus_message_unref (reply); + return; + } + + if (reply) + { + gchar *app_name, *obj_path; + + if (strcmp (dbus_message_get_signature (reply), "(so)") != 0) + { + g_warning ("AT-SPI: Could not obtain desktop path or name\n"); + } + else + { + DBusMessageIter iter, iter_struct; + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &app_name); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &obj_path); + + g_free (app->desktop_name); + app->desktop_name = g_strdup (app_name); + g_free (app->desktop_path); + app->desktop_path = g_strdup (obj_path); + } + } + else + { + g_warning ("AT-SPI: Could not embed inside desktop"); + return; + } + dbus_message_unref (reply); + + if (!spi_global_app_data->events_initialized) + get_registered_event_listeners (spi_global_app_data); +} + +static gboolean +register_application (SpiBridge * app) +{ + DBusMessage *message; + DBusMessageIter iter; + DBusPendingCall *pending; + + g_free (app->desktop_name); + g_free (app->desktop_path); + + /* These will be overridden when we get a reply, but in practice these + defaults should always be correct */ + app->desktop_name = g_strdup (ATSPI_DBUS_NAME_REGISTRY); + app->desktop_path = g_strdup (ATSPI_DBUS_PATH_ROOT); + + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_ROOT, + ATSPI_DBUS_INTERFACE_SOCKET, + "Embed"); + + dbus_message_iter_init_append (message, &iter); + spi_object_append_reference (&iter, app->root); + + if (!dbus_connection_send_with_reply (app->bus, message, &pending, -1) + || !pending) + { + if (pending) + dbus_pending_call_unref (pending); + + dbus_message_unref (message); + return FALSE; + } + + dbus_pending_call_set_notify (pending, register_reply, app, NULL); + + if (message) + dbus_message_unref (message); + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +static void +deregister_application (SpiBridge * app) +{ + DBusMessage *message; + DBusMessageIter iter; + const char *uname; + + message = dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_REGISTRY, + ATSPI_DBUS_INTERFACE_REGISTRY, + "DeregisterApplication"); + dbus_message_set_no_reply (message, TRUE); + + uname = dbus_bus_get_unique_name (app->bus); + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &uname); + dbus_connection_send (app->bus, message, NULL); + if (message) + dbus_message_unref (message); + + if (app->app_bus_addr) + { + unlink (app->app_bus_addr); + g_free (app->app_bus_addr); + app->app_bus_addr = NULL; + } + + if (app->app_tmp_dir) + { + rmdir (app->app_tmp_dir); + g_free (app->app_tmp_dir); + app->app_tmp_dir = NULL; + } + + g_free (app->desktop_name); + app->desktop_name = NULL; + g_free (app->desktop_path); + app->desktop_path = NULL; +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ + +static AtkPlugClass *plug_class; +static AtkSocketClass *socket_class; + +static gchar * +get_plug_id (AtkPlug * plug) +{ + const char *uname = dbus_bus_get_unique_name (spi_global_app_data->bus); + gchar *path; + GString *str = g_string_new (NULL); + + path = spi_register_object_to_path (spi_global_register, G_OBJECT (plug)); + g_string_printf (str, "%s:%s", uname, path); + g_free (path); + return g_string_free (str, FALSE); +} + +AtkStateSet * +socket_ref_state_set (AtkObject *accessible) +{ + char *child_name, *child_path; + AtkSocket *socket = ATK_SOCKET (accessible); + int count = 0; + int j; + int v; + DBusMessage *message, *reply; + DBusMessageIter iter, iter_array; + AtkStateSet *set; + + set = atk_state_set_new (); + + if (!socket->embedded_plug_id) + return set; + + child_name = g_strdup (socket->embedded_plug_id); + if (!child_name) + return set; + child_path = g_utf8_strchr (child_name + 1, -1, ':'); + if (!child_path) + { + g_free (child_name); + return set; + } + *(child_path++) = '\0'; + message = dbus_message_new_method_call (child_name, child_path, ATSPI_DBUS_INTERFACE_ACCESSIBLE, "GetState"); + g_free (child_name); + reply = dbus_connection_send_with_reply_and_block (spi_global_app_data->bus, message, 1, NULL); + dbus_message_unref (message); + if (reply == NULL) + return set; + if (strcmp (dbus_message_get_signature (reply), "au") != 0) + { + dbus_message_unref (reply); + return set; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + do + { + dbus_message_iter_get_basic (&iter_array, &v); + for (j = 0; j < 32; j++) + { + if (v & (1 << j)) + { + AtkState state = spi_atk_state_from_spi_state ((count << 5) + j); + atk_state_set_add_state (set, state); + } + } + count++; + } + while (dbus_message_iter_next (&iter_array)); + dbus_message_unref (reply); + return set; +} + +static void +socket_embed_hook (AtkSocket * socket, gchar * plug_id) +{ + g_return_if_fail (spi_global_register != NULL); + + AtkObject *accessible = ATK_OBJECT(socket); + gchar *plug_name, *plug_path; + AtkObjectClass *klass; + + /* Force registration */ + gchar *path = spi_register_object_to_path (spi_global_register, G_OBJECT (accessible)); + /* Let the plug know that it has been embedded */ + plug_name = g_strdup (plug_id); + if (!plug_name) + { + g_free (path); + return; + } + plug_path = g_utf8_strchr (plug_name + 1, -1, ':'); + if (plug_path) + { + DBusMessage *message; + *(plug_path++) = '\0'; + message = dbus_message_new_method_call (plug_name, plug_path, ATSPI_DBUS_INTERFACE_SOCKET, "Embedded"); + dbus_message_append_args (message, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); + dbus_connection_send (spi_global_app_data->bus, message, NULL); + } + g_free (plug_name); + g_free (path); + + klass = ATK_OBJECT_GET_CLASS (accessible); + klass->ref_state_set = socket_ref_state_set; +} + +static void +install_plug_hooks () +{ + gpointer data; + + data = g_type_class_ref (ATK_TYPE_PLUG); + plug_class = ATK_PLUG_CLASS (data); + data = g_type_class_ref (ATK_TYPE_SOCKET); + socket_class = ATK_SOCKET_CLASS (data); + plug_class->get_object_id = get_plug_id; + socket_class->embed = socket_embed_hook; +} + +static guint +get_ancestral_uid (guint pid) +{ + FILE *fp; + char buf [80]; + int ppid = 0; + int uid = 0; + gboolean got_ppid = 0; + gboolean got_uid = 0; + + sprintf (buf, "/proc/%d/status", pid); + fp = fopen (buf, "r"); + if (!fp) + return 0; + while ((!got_ppid || !got_uid) && fgets (buf, sizeof (buf), fp)) + { + if (sscanf (buf, "PPid:\t%d", &ppid) == 1) + got_ppid = TRUE; + else if (sscanf (buf, "Uid:\t%d", &uid) == 1) + got_uid = TRUE; + } + fclose (fp); + + if (!got_ppid || !got_uid) + return 0; + if (uid != 0) + return uid; + if (ppid == 0 || ppid == 1) + return 0; + return get_ancestral_uid (ppid); +} + +static dbus_bool_t +user_check (DBusConnection *bus, unsigned long uid, void *data) +{ + if (uid == getuid () || uid == geteuid ()) + return TRUE; + if (getuid () == 0) + return get_ancestral_uid (getpid ()) == uid; + return FALSE; +} + +static void +new_connection_cb (DBusServer *server, DBusConnection *con, void *data) +{ + dbus_connection_set_unix_user_function (con, user_check, NULL, NULL); + dbus_connection_ref(con); + atspi_dbus_connection_setup_with_g_main(con, NULL); + droute_intercept_dbus (con); + droute_context_register (spi_global_app_data->droute, con); + + spi_global_app_data->direct_connections = g_list_append (spi_global_app_data->direct_connections, con); +} + + +gchar *atspi_dbus_name = NULL; +static gboolean atspi_no_register = FALSE; + +static GOptionEntry atspi_option_entries[] = { + {"atspi-dbus-name", 0, 0, G_OPTION_ARG_STRING, &atspi_dbus_name, + "D-Bus bus name to register as", NULL}, + {"atspi-no-register", 0, 0, G_OPTION_ARG_NONE, &atspi_no_register, + "Do not register with Registry Daemon", NULL}, + {NULL} +}; + +static gchar * +introspect_children_cb (const char *path, void *data) +{ + if (!strcmp (path, "/org/a11y/atspi/accessible")) + { + return g_strdup ("<node name=\"root\"/>\n"); + /* TODO: Should we place the whole hierarchy here? */ + } + return NULL; +} + +static void +handle_event_listener_registered (DBusConnection *bus, DBusMessage *message, + void *user_data) +{ + DBusMessageIter iter; + const char *signature = dbus_message_get_signature (message); + + if (strcmp (signature, "ssas") != 0 && + strcmp (signature, "ss") != 0) + { + g_warning ("got RegisterEvent with invalid signature '%s'", signature); + return; + } + + dbus_message_iter_init (message, &iter); + add_event_from_iter (&iter); +} + +static void +free_property_definition (void *data) +{ + AtspiPropertyDefinition *pd = data; + + g_free (pd->name); + g_free (pd); +} + +static void +remove_events (const char *bus_name, const char *event) +{ + gchar **remove_data; + GList *list; + + remove_data = g_strsplit (event, ":", 3); + if (!remove_data) + { + return; + } + + for (list = spi_global_app_data->events; list;) + { + event_data *evdata = list->data; + if (!g_strcmp0 (evdata->bus_name, bus_name) && + spi_event_is_subtype (evdata->data, remove_data)) + { + GList *events = spi_global_app_data->events; + g_strfreev (evdata->data); + g_free (evdata->bus_name); + g_slist_free_full (evdata->properties, free_property_definition); + g_free (evdata); + if (list->prev) + { + GList *next = list->next; + list->prev = g_list_remove (list->prev, evdata); + list = next; + } + else + { + spi_global_app_data->events = g_list_remove (events, evdata); + list = spi_global_app_data->events; + } + } + else + { + list = list->next; + } + } + + g_strfreev (remove_data); +} + +static void +handle_event_listener_deregistered (DBusConnection *bus, DBusMessage *message, + void *user_data) +{ + gchar *name; + char *sender; + + if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &sender, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) + return; + + remove_events (sender, name); +} + +static void +handle_device_listener_registered (DBusConnection *bus, DBusMessage *message, + void *user_data) +{ + char *sender; + DBusMessageIter iter, iter_struct; + + if (strncmp (dbus_message_get_signature (message), "(s", 2) != 0) + { + g_warning ("atk-bridge: handle_device_listener_register: unknown signature"); + return; + } + + dbus_message_iter_init (message, &iter); + dbus_message_iter_recurse (&iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &sender); + spi_atk_add_client (sender); +} + +static DBusHandlerResult +signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + const char *interface = dbus_message_get_interface (message); + const char *member = dbus_message_get_member (message); + DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + static gboolean registry_lost = FALSE; + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (!strcmp (interface, ATSPI_DBUS_INTERFACE_REGISTRY)) + { + result = DBUS_HANDLER_RESULT_HANDLED; + if (!strcmp (member, "EventListenerRegistered")) + handle_event_listener_registered (bus, message, user_data); + else if (!strcmp (member, "EventListenerDeregistered")) + handle_event_listener_deregistered (bus, message, user_data); + else + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else if (!strcmp (interface, ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER)) + { + result = DBUS_HANDLER_RESULT_HANDLED; + if (!strcmp (member, "KeystrokeListenerRegistered")) + handle_device_listener_registered (bus, message, user_data); + else if (!strcmp (member, "DeviceListenerRegistered")) + handle_device_listener_registered (bus, message, user_data); + else + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (!g_strcmp0(interface, DBUS_INTERFACE_DBUS) && + !g_strcmp0(member, "NameOwnerChanged")) + { + char *name, *old, *new; + if (dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + { + if (!strcmp (name, "org.a11y.atspi.Registry")) + { + if (registry_lost && !old[0]) + { + register_application (spi_global_app_data); + registry_lost = FALSE; + } + else if (!new[0]) + registry_lost = TRUE; + } + else if (*old != '\0' && *new == '\0') + spi_atk_remove_client (old); + } + } + + return result; +} + +int +spi_atk_create_socket (SpiBridge *app) +{ +#ifndef DISABLE_P2P + DBusServer *server; + DBusError error; + + if (getuid () != 0) + { + app->app_tmp_dir = g_build_filename (g_get_user_runtime_dir (), + "at-spi2-XXXXXX", NULL); + if (!g_mkdtemp (app->app_tmp_dir)) + { + g_free (app->app_tmp_dir); + app->app_tmp_dir = NULL; + return FALSE; + } + } + + if (app->app_tmp_dir) + app->app_bus_addr = g_strdup_printf ("unix:path=%s/socket", app->app_tmp_dir); + else + app->app_bus_addr = g_strdup_printf ("unix:path=%s/at-spi2-socket-%d", + g_get_user_runtime_dir (), getpid ()); + + if (!spi_global_app_data->app_bus_addr) + return -1; + + dbus_error_init(&error); + server = dbus_server_listen(spi_global_app_data->app_bus_addr, &error); + if (server == NULL) + { + g_warning ("atk-bridge: Couldn't listen on dbus server: %s", error.message); + dbus_error_free (&error); + spi_global_app_data->app_bus_addr [0] = '\0'; + g_main_context_unref (spi_global_app_data->main_context); + spi_global_app_data->main_context = NULL; + return -1; + } + + atspi_dbus_server_setup_with_g_main(server, NULL); + dbus_server_set_new_connection_function(server, new_connection_cb, NULL, NULL); + + spi_global_app_data->server = server; +#endif + + return 0; +} + +/* + * Checks the status of the environment variables + * + * At this moment it only checks NO_AT_BRIDGE + * + * Returns TRUE if there isn't anything on the environment preventing + * you to load the bridge, FALSE otherwise + */ +static gboolean +check_envvar (void) +{ + const gchar *envvar; + + envvar = g_getenv ("NO_AT_BRIDGE"); + + if (envvar && atoi (envvar) == 1) + return FALSE; + else + return TRUE; +} + +void +spi_atk_activate () +{ + DRoutePath *treepath; + + spi_atk_register_event_listeners (); + if (!spi_global_cache) + { + spi_global_cache = g_object_new (SPI_CACHE_TYPE, NULL); + treepath = droute_add_one (spi_global_app_data->droute, + "/org/a11y/atspi/cache", spi_global_cache); + + if (!treepath) + { + g_warning ("atk-bridge: Error in droute_add_one(). Already running?"); + return; + } + spi_initialize_cache (treepath); + if (spi_global_app_data->bus) + droute_path_register (treepath, spi_global_app_data->bus); + } +} + +/* + * spi_app_init + * + * The following needs to be initialized. + * + * - DRoute for routing message to their accessible objects. + * - Event handlers for emmitting signals on specific ATK events. + * - setup the bus for p2p communication + * - Application registration with the AT-SPI registry. + * + */ +int +atk_bridge_adaptor_init (gint * argc, gchar ** argv[]) +{ + GOptionContext *opt; + GError *err = NULL; + DBusError error; + AtkObject *root; + gboolean load_bridge; + DRoutePath *accpath; + + load_bridge = check_envvar (); + if (inited && !load_bridge) + g_warning ("ATK Bridge is disabled but a11y has already been enabled."); + + if (inited || !load_bridge) + return 0; + + inited = TRUE; + + root = atk_get_root (); + g_warn_if_fail (root); + if (!root) + { + inited = FALSE; + return -1; + } + + /* Parse command line options */ + opt = g_option_context_new (NULL); + g_option_context_add_main_entries (opt, atspi_option_entries, NULL); + g_option_context_set_ignore_unknown_options (opt, TRUE); + if (!g_option_context_parse (opt, argc, argv, &err)) + { + g_warning ("AT-SPI Option parsing failed: %s\n", err->message); + g_error_free (err); + } + g_option_context_free (opt); + + /* Allocate global data and do ATK initializations */ + spi_global_app_data = g_new0 (SpiBridge, 1); + spi_global_app_data->root = g_object_ref (root); + + /* Set up D-Bus connection and register bus name */ + dbus_error_init (&error); + spi_global_app_data->bus = atspi_get_a11y_bus (); + if (!spi_global_app_data->bus) + { + g_free (spi_global_app_data); + spi_global_app_data = NULL; + inited = FALSE; + return -1; + } + + if (atspi_dbus_name != NULL) + { + if (dbus_bus_request_name + (spi_global_app_data->bus, atspi_dbus_name, 0, &error)) + { + g_print ("AT-SPI Recieved D-Bus name - %s\n", atspi_dbus_name); + } + else + { + g_print + ("AT-SPI D-Bus name requested but could not be allocated - %s\n", + atspi_dbus_name); + } + } + + spi_global_app_data->main_context = g_main_context_new (); + + atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, NULL); + + /* Hook our plug-and socket functions */ + install_plug_hooks (); + + /* + * Create the leasing, register and cache objects. + * The order is important here, the cache depends on the + * register object. + */ + spi_global_register = g_object_new (SPI_REGISTER_TYPE, NULL); + spi_global_leasing = g_object_new (SPI_LEASING_TYPE, NULL); + + /* Register droute for routing AT-SPI messages */ + spi_global_app_data->droute = + droute_new (); + + accpath = droute_add_many (spi_global_app_data->droute, + "/org/a11y/atspi/accessible", + NULL, + introspect_children_cb, + NULL, + (DRouteGetDatumFunction) + spi_global_register_path_to_object); + + + /* Register all interfaces with droute and set up application accessible db */ + spi_initialize_accessible (accpath); + spi_initialize_application (accpath); + spi_initialize_action (accpath); + spi_initialize_collection (accpath); + spi_initialize_component (accpath); + spi_initialize_document (accpath); + spi_initialize_editabletext (accpath); + spi_initialize_hyperlink (accpath); + spi_initialize_hypertext (accpath); + spi_initialize_image (accpath); + spi_initialize_selection (accpath); + spi_initialize_socket (accpath); + spi_initialize_table (accpath); + spi_initialize_table_cell (accpath); + spi_initialize_text (accpath); + spi_initialize_value (accpath); + + droute_context_register (spi_global_app_data->droute, + spi_global_app_data->bus); + + /* Register methods to send D-Bus signals on certain ATK events */ + if (clients) + spi_atk_activate (); + + /* Set up filter and match rules to catch signals */ + dbus_bus_add_match (spi_global_app_data->bus, "type='signal', interface='org.a11y.atspi.Registry', sender='org.a11y.atspi.Registry'", NULL); + dbus_bus_add_match (spi_global_app_data->bus, "type='signal', interface='org.a11y.atspi.DeviceEventListener', sender='org.a11y.atspi.Registry'", NULL); + dbus_bus_add_match (spi_global_app_data->bus, "type='signal', arg0='org.a11y.atspi.Registry', interface='org.freedesktop.DBus', member='NameOwnerChanged'", NULL); + dbus_connection_add_filter (spi_global_app_data->bus, signal_filter, NULL, + NULL); + + /* Register this app by sending a signal out to AT-SPI registry daemon */ + if (!atspi_no_register && (!root || !ATK_IS_PLUG (root))) + register_application (spi_global_app_data); + else + get_registered_event_listeners (spi_global_app_data); + + dbus_error_free (&error); + return 0; +} + +void +atk_bridge_adaptor_cleanup (void) +{ + GList *l; + GSList *ls; + + if (!inited) + return; + + if (!spi_global_app_data) + return; + + spi_atk_tidy_windows (); + spi_atk_deregister_event_listeners (); + + deregister_application (spi_global_app_data); + + if (spi_global_app_data->bus) + { + dbus_connection_remove_filter (spi_global_app_data->bus, signal_filter, NULL); + droute_context_unregister (spi_global_app_data->droute, spi_global_app_data->bus); + dbus_connection_close (spi_global_app_data->bus); + dbus_connection_unref (spi_global_app_data->bus); + spi_global_app_data->bus = NULL; + } + + for (l = spi_global_app_data->direct_connections; l; l = l->next) + { + DBusConnection *connection; + + connection = l->data; + + droute_context_unregister (spi_global_app_data->droute, connection); + droute_unintercept_dbus (connection); + dbus_connection_close (connection); + dbus_connection_unref (connection); + } + g_list_free (spi_global_app_data->direct_connections); + spi_global_app_data->direct_connections = NULL; + + for (ls = clients; ls; ls = ls->next) + g_free (ls->data); + g_slist_free (clients); + clients = NULL; + + g_clear_object (&spi_global_cache); + g_clear_object (&spi_global_leasing); + g_clear_object (&spi_global_register); + + if (spi_global_app_data->main_context) + g_main_context_unref (spi_global_app_data->main_context); + + droute_free (spi_global_app_data->droute); + + g_free (spi_global_app_data); + spi_global_app_data = NULL; + + inited = FALSE; +} + +/*---------------------------------------------------------------------------*/ + +static gchar *name_match_tmpl = + "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged', arg0='%s'"; + +void +spi_atk_add_client (const char *bus_name) +{ + GSList *l; + gchar *match; + + for (l = clients; l; l = l->next) + { + if (!g_strcmp0 (l->data, bus_name)) + return; + } + if (!clients) + spi_atk_activate (); + clients = g_slist_append (clients, g_strdup (bus_name)); + match = g_strdup_printf (name_match_tmpl, bus_name); + dbus_bus_add_match (spi_global_app_data->bus, match, NULL); + g_free (match); +} + +void +spi_atk_remove_client (const char *bus_name) +{ + GSList *l; + GSList *next_node; + + l = clients; + while (l) + { + next_node = l->next; + + if (!g_strcmp0 (l->data, bus_name)) + { + gchar *match = g_strdup_printf (name_match_tmpl, l->data); + dbus_bus_remove_match (spi_global_app_data->bus, match, NULL); + g_free (match); + g_free (l->data); + clients = g_slist_delete_link (clients, l); + if (!clients) + spi_atk_deregister_event_listeners (); + return; + } + + l = next_node; + } +} + +void +spi_atk_add_interface (DRoutePath *path, + const char *name, + const char *introspect, + const DRouteMethod *methods, + const DRouteProperty *properties) +{ + droute_path_add_interface (path, name, introspect, methods, properties); + + if (properties) + { + if (!spi_global_app_data->property_hash) + spi_global_app_data->property_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, NULL); + g_hash_table_insert (spi_global_app_data->property_hash, g_strdup (name), + (gpointer) properties); + } +} +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/bridge.h b/atk-adaptor/bridge.h new file mode 100644 index 0000000..64882e4 --- /dev/null +++ b/atk-adaptor/bridge.h @@ -0,0 +1,97 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. + * Copyright 2008, 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef BRIDGE_H +#define BRIDGE_H + +#include <atk/atk.h> +#include <droute/droute.h> + +typedef struct _SpiBridge SpiBridge; +typedef struct _SpiBridgeClass SpiBridgeClass; + +G_BEGIN_DECLS + +typedef struct _AtspiPropertyDefinition AtspiPropertyDefinition; +struct _AtspiPropertyDefinition +{ + char *name; + GType type; + DRoutePropertyFunction func; +}; + +typedef struct _event_data event_data; +struct _event_data +{ + gchar *bus_name; + gchar **data; + GSList *properties; +}; + +struct _SpiBridge +{ + GObject parent; + + AtkObject *root; + + DBusConnection *bus; + DRouteContext *droute; + GMainContext *main_context; + DBusServer *server; + GList *direct_connections; + +/* + SpiRegister *reg; + SpiCache *cache; + SpiLeasing *leasing; +*/ + gchar *desktop_name; + gchar *desktop_path; +gchar *app_tmp_dir; +gchar *app_bus_addr; + GList *events; + gboolean events_initialized; + GHashTable *property_hash; +}; + +extern SpiBridge *spi_global_app_data; + +void spi_atk_add_client (const char *bus_name); +void spi_atk_remove_client (const char *bus_name); + +int spi_atk_create_socket (SpiBridge *app); + +void spi_atk_add_interface (DRoutePath *path, + const char *name, + const char *introspect, + const DRouteMethod *methods, + const DRouteProperty *properties); + +DRoutePropertyFunction _atk_bridge_find_property_func (const char *property, + GType *type); + +GType _atk_bridge_type_from_iface (const char *iface); +G_END_DECLS + +#endif /* BRIDGE_H */ diff --git a/atk-adaptor/event.c b/atk-adaptor/event.c new file mode 100644 index 0000000..0662664 --- /dev/null +++ b/atk-adaptor/event.c @@ -0,0 +1,1366 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2011, F123 Consulting & Mais Diferenças + * Copyright 2008, 2009, Codethink Ltd. + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <ctype.h> + +#include <atk/atk.h> +#include <droute/droute.h> +#include <atspi/atspi.h> + +#include "bridge.h" +#include "accessible-register.h" + +#include "spi-dbus.h" +#include "event.h" +#include "object.h" + +static GArray *listener_ids = NULL; + +static gint atk_bridge_key_event_listener_id; +static gint atk_bridge_focus_tracker_id; + +/*---------------------------------------------------------------------------*/ + +#define ITF_EVENT_OBJECT "org.a11y.atspi.Event.Object" +#define ITF_EVENT_WINDOW "org.a11y.atspi.Event.Window" +#define ITF_EVENT_DOCUMENT "org.a11y.atspi.Event.Document" +#define ITF_EVENT_FOCUS "org.a11y.atspi.Event.Focus" + +/*---------------------------------------------------------------------------*/ + +typedef struct _SpiReentrantCallClosure +{ + DBusConnection *bus; + GMainLoop *loop; + DBusMessage *reply; + guint timeout; +} SpiReentrantCallClosure; + +static void +switch_main_context (GMainContext *cnx) +{ + GList *list; + + if (spi_global_app_data->server) + atspi_dbus_server_setup_with_g_main (spi_global_app_data->server, cnx); + atspi_dbus_connection_setup_with_g_main (spi_global_app_data->bus, cnx); + atspi_set_main_context (cnx); + for (list = spi_global_app_data->direct_connections; list; list = list->next) + atspi_dbus_connection_setup_with_g_main (list->data, cnx); +} + +static void +set_reply (DBusPendingCall * pending, void *user_data) +{ + SpiReentrantCallClosure* closure = (SpiReentrantCallClosure *) user_data; + + closure->reply = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + switch_main_context (NULL); + g_main_loop_quit (closure->loop); +} + +static gboolean +timeout_reply (void *data) +{ + SpiReentrantCallClosure *closure = data; + + switch_main_context (NULL); + g_main_loop_quit (closure->loop); + closure->timeout = -1; + return FALSE; +} + +static DBusMessage * +send_and_allow_reentry (DBusConnection * bus, DBusMessage * message) +{ + DBusPendingCall *pending; + SpiReentrantCallClosure closure; + GSource *source; + + closure.bus = bus; + closure.loop = g_main_loop_new (spi_global_app_data->main_context, FALSE); + closure.reply = NULL; + switch_main_context (spi_global_app_data->main_context); + + if (!dbus_connection_send_with_reply (bus, message, &pending, 9000) || !pending) + { + switch_main_context (NULL); + return NULL; + } + dbus_pending_call_set_notify (pending, set_reply, (void *) &closure, NULL); + source = g_timeout_source_new (500); + g_source_set_callback (source, timeout_reply, &closure, NULL); + closure.timeout = g_source_attach (source, spi_global_app_data->main_context); + g_source_unref (source); + g_main_loop_run (closure.loop); + if (closure.timeout != -1) + g_source_destroy (source); + + g_main_loop_unref (closure.loop); + if (!closure.reply) + dbus_pending_call_cancel (pending); + return closure.reply; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Functionality related to sending device events from the application. + * + * This is used for forwarding key events on to the registry daemon. + */ + +static gboolean +Accessibility_DeviceEventController_NotifyListenersSync (const + AtspiDeviceEvent + * key_event) +{ + DBusMessage *message; + dbus_bool_t consumed = FALSE; + + message = + dbus_message_new_method_call (SPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_DEC, + ATSPI_DBUS_INTERFACE_DEC, + "NotifyListenersSync"); + + if (spi_dbus_marshal_deviceEvent (message, key_event)) + { + DBusMessage *reply = + send_and_allow_reentry (spi_global_app_data->bus, message); + if (reply) + { + DBusError error; + dbus_error_init (&error); + if (!dbus_message_get_args (reply, &error, DBUS_TYPE_BOOLEAN, + &consumed, DBUS_TYPE_INVALID)) + { + /* TODO: print a warning */ + dbus_error_free (&error); + } + dbus_message_unref (reply); + } + } + dbus_message_unref (message); + return consumed; +} + +static void +spi_init_keystroke_from_atk_key_event (AtspiDeviceEvent * keystroke, + AtkKeyEventStruct * event) +{ + keystroke->id = (dbus_int32_t) event->keyval; + keystroke->hw_code = (dbus_int16_t) event->keycode; + keystroke->timestamp = (dbus_uint32_t) event->timestamp; + keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF); + if (event->string) + { + gunichar c; + + keystroke->event_string = g_strdup (event->string); + c = g_utf8_get_char_validated (event->string, -1); + if (c > 0 && g_unichar_isprint (c)) + keystroke->is_text = TRUE; + else + keystroke->is_text = FALSE; + } + else + { + keystroke->event_string = g_strdup (""); + keystroke->is_text = FALSE; + } + switch (event->type) + { + case (ATK_KEY_EVENT_PRESS): + keystroke->type = ATSPI_KEY_PRESSED; + break; + case (ATK_KEY_EVENT_RELEASE): + keystroke->type = ATSPI_KEY_RELEASED; + break; + default: + keystroke->type = 0; + break; + } +#if 0 + g_print + ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n", + (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code, + (int) keystroke->modifiers, keystroke->event_string, + (int) keystroke->is_text, (unsigned long) keystroke->timestamp); +#endif +} + + +static gint +spi_atk_bridge_key_listener (AtkKeyEventStruct * event, gpointer data) +{ + gboolean result; + AtspiDeviceEvent key_event; + + spi_init_keystroke_from_atk_key_event (&key_event, event); + + result = + Accessibility_DeviceEventController_NotifyListenersSync (&key_event); + + if (key_event.event_string) + g_free (key_event.event_string); + + return result; +} + +/*---------------------------------------------------------------------------*/ + +static const void * +validate_for_dbus (const gint type, + const void *val) +{ + switch (type) + { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + if (!val) + return ""; + else if (!g_utf8_validate (val, -1, NULL)) + { + g_warning ("atk-bridge: Received bad UTF-8 string when emitting event"); + return ""; + } + else + return val; + default: + return val; + } +} + +static void +append_basic (DBusMessageIter *iter, + const char *type, + const void *val) +{ + DBusMessageIter sub; + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &sub); + + val = validate_for_dbus ((int) *type, val); + dbus_message_iter_append_basic(&sub, (int) *type, &val); + + dbus_message_iter_close_container(iter, &sub); +} + +static void +append_rect (DBusMessageIter *iter, + const char *type, + const void *val) +{ + DBusMessageIter variant, sub; + const AtkRectangle *rect = (const AtkRectangle *) val; + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, type, &variant); + + dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub); + + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x)); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y)); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width)); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height)); + + dbus_message_iter_close_container (&variant, &sub); + + dbus_message_iter_close_container(iter, &variant); +} + +static void +append_object (DBusMessageIter *iter, + const char *type, + const void *val) +{ + spi_object_append_v_reference (iter, ATK_OBJECT (val)); +} + +static gchar * +signal_name_to_dbus (const gchar *s) +{ + gchar *ret = g_strdup (s); + gchar *t; + + if (!ret) + return NULL; + ret [0] = toupper (ret [0]); + while ((t = strchr (ret, '-')) != NULL) + { + memmove (t, t + 1, strlen (t)); + *t = toupper (*t); + } + return ret; +} + +/* + * Converts names of the form "active-descendant-changed" to + * "ActiveDescendantChanged" + */ +static gchar * +ensure_proper_format (const char *name) +{ + gchar *ret = (gchar *) g_malloc (strlen (name) * 2 + 2); + gchar *p = ret; + gboolean need_upper = TRUE; + + if (!ret) + return NULL; + while (*name) + { + if (need_upper) + { + *p++ = toupper (*name); + need_upper = FALSE; + } + else if (*name == '-') + need_upper = TRUE; + else if (*name == ':') + { + need_upper = TRUE; + *p++ = *name; + } + else + *p++ = *name; + name++; + } + *p = '\0'; + return ret; +} + +void +append_properties (GArray *properties, event_data *evdata) +{ + GSList *ls; + gint i; + + for (ls = evdata->properties; ls; ls = ls->next) + { + gboolean dup = FALSE; + for (i = 0; i < properties->len; i++) + { + if (ls->data == g_array_index (properties, AtspiPropertyDefinition *, i)) + { + dup = TRUE; + break; + } + } + if (!dup) + g_array_append_val (properties, ls->data); + } +} + +static gboolean +signal_is_needed (const gchar *klass, const gchar *major, const gchar *minor, + GArray **properties) +{ + gchar *data [4]; + event_data *evdata; + gboolean ret = FALSE; + GList *list; + GArray *props = NULL; + + if (!spi_global_app_data->events_initialized) + return TRUE; + + data [0] = ensure_proper_format (klass + 21); + data [1] = ensure_proper_format (major); + data [2] = ensure_proper_format (minor); + data [3] = NULL; + + /* Hack: Always pass events that update the cache. + * TODO: FOr 2.2, have at-spi2-core define a special "cache listener" for + * this instead, so that we don't send these if no one is listening */ + if (!g_strcmp0 (data [1], "ChildrenChanged") || + ((!g_strcmp0 (data [1], "PropertyChange")) && + (!g_strcmp0 (data [2], "accessible-name") || + !g_strcmp0 (data [2], "accessible-description") || + !g_strcmp0 (data [2], "accessible-parent") || + !g_strcmp0 (data [2], "accessible-role"))) || + !g_strcmp0 (data [1], "StateChanged")) + ret = TRUE; + + /* Hack: events such as "object::text-changed::insert:system" as + generated by Gecko */ + data [2][strcspn (data [2], ":")] = '\0'; + + for (list = spi_global_app_data->events; list; list = list->next) + { + evdata = list->data; + if (spi_event_is_subtype (data, evdata->data)) + { + ret = TRUE; + if (!props) + props = g_array_new (TRUE, TRUE, sizeof (AtspiPropertyDefinition *)); + append_properties (props, evdata); + } + } + + g_free (data [2]); + g_free (data [1]); + g_free (data [0]); + *properties = props; + return ret; +} + +/* Convert a : to a / so that listeners can use arg0path to match only + * * the prefix */ +static char * +adapt_minor_for_dbus (const char *source) +{ + gchar *ret = g_strdup (source); + int i = strcspn (ret, ":"); + if (ret[i] == ':') + ret[i] = '/'; + return ret; +} + +static void +open_variant (DBusMessageIter *iter, const char *name, const char *type, + DBusMessageIter *out) +{ + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &name); + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, type, out); +} + +/* + * Emits an AT-SPI event. + * AT-SPI events names are split into three parts: + * class:major:minor + * This is mapped onto D-Bus events as: + * D-Bus Interface:Signal Name:Detail argument + * + * Marshals a basic type into the 'any_data' attribute of + * the AT-SPI event. + */ +static void +emit_event (AtkObject *obj, + const char *klass, + const char *major, + const char *minor, + dbus_int32_t detail1, + dbus_int32_t detail2, + const char *type, + const void *val, + void (*append_variant) (DBusMessageIter *, const char *, const void *)) +{ + DBusConnection *bus = spi_global_app_data->bus; + char *path; + char *minor_dbus; + + gchar *cname; + DBusMessage *sig; + DBusMessageIter iter, iter_dict, iter_dict_entry, iter_variant, iter_array; + GArray *properties = NULL; + + if (!klass) klass = ""; + if (!major) major = ""; + if (!minor) minor = ""; + if (!type) type = "u"; + + if (!signal_is_needed (klass, major, minor, &properties)) + return; + + path = spi_register_object_to_path (spi_global_register, G_OBJECT (obj)); + g_return_if_fail (path != NULL); + + /* + * This is very annoying, but as '-' isn't a legal signal + * name in D-Bus (Why not??!?) The names need converting + * on this side, and again on the client side. + */ + cname = signal_name_to_dbus (major); + sig = dbus_message_new_signal(path, klass, cname); + + dbus_message_iter_init_append(sig, &iter); + + minor_dbus = adapt_minor_for_dbus (minor); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor_dbus); + g_free (minor_dbus); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2); + append_variant (&iter, type, val); + + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict); + /* Add requested properties, unless the object is being marked defunct, in + which case it's safest not to touch it */ + if (minor == NULL || strcmp (minor, "defunct") != 0 || detail1 == 0) + { + if (properties) + { + gint i; + for (i = 0; i < properties->len; i++) + { + AtspiPropertyDefinition *prop = g_array_index (properties, AtspiPropertyDefinition *, i); + dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, + &iter_dict_entry); + dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop->name); + prop->func (&iter_dict_entry, obj); + dbus_message_iter_close_container (&iter_dict, &iter_dict_entry); + } + g_array_free (properties, TRUE); + } + } + dbus_message_iter_close_container (&iter, &iter_dict); + + dbus_connection_send(bus, sig, NULL); + dbus_message_unref(sig); + + if (g_strcmp0 (cname, "ChildrenChanged") != 0) + spi_object_lease_if_needed (G_OBJECT (obj)); + + g_free(cname); + g_free (path); +} + +/*---------------------------------------------------------------------------*/ + +/* + * The focus listener handles the ATK 'focus' signal and forwards it + * as the AT-SPI event, 'focus:' + */ +static void +focus_tracker (AtkObject * accessible) +{ + emit_event (accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); +} + +/*---------------------------------------------------------------------------*/ + +#define PCHANGE "PropertyChange" + +/* + * This handler handles the following ATK signals and + * converts them to AT-SPI events: + * + * Gtk:AtkObject:property-change -> object:property-change:(property-name) + * + * The property-name is part of the ATK property-change signal. + */ +static gboolean +property_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + AtkPropertyValues *values; + + const gchar *pname = NULL; + + AtkObject *otemp; + const gchar *s1; + gint i; + + accessible = g_value_get_object (¶m_values[0]); + values = (AtkPropertyValues *) g_value_get_pointer (¶m_values[1]); + + pname = values[0].property_name; + + /* TODO Could improve this control statement by matching + * on only the end of the signal names, + */ + if (strcmp (pname, "accessible-name") == 0) + { + s1 = atk_object_get_name (accessible); + if (s1 != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); + } + else if (strcmp (pname, "accessible-description") == 0) + { + s1 = atk_object_get_description (accessible); + if (s1 != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); + } + else if (strcmp (pname, "accessible-parent") == 0) + { + otemp = atk_object_get_parent (accessible); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); + } + else if (strcmp (pname, "accessible-role") == 0) + { + i = atk_object_get_role (accessible); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_UINT32_AS_STRING, GINT_TO_POINTER(i), append_basic); + } + else if (strcmp (pname, "accessible-table-summary") == 0) + { + otemp = atk_table_get_summary (ATK_TABLE (accessible)); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); + } + else if (strcmp (pname, "accessible-table-column-header") == 0) + { + i = g_value_get_int (&(values->new_value)); + otemp = atk_table_get_column_header (ATK_TABLE (accessible), i); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); + } + else if (strcmp (pname, "accessible-table-row-header") == 0) + { + i = g_value_get_int (&(values->new_value)); + otemp = atk_table_get_row_header (ATK_TABLE (accessible), i); + if (otemp != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); + } + else if (strcmp (pname, "accessible-table-row-description") == 0) + { + i = g_value_get_int (&(values->new_value)); + s1 = atk_table_get_row_description (ATK_TABLE (accessible), i); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); + } + else if (strcmp (pname, "accessible-table-column-description") == 0) + { + i = g_value_get_int (&(values->new_value)); + s1 = atk_table_get_column_description (ATK_TABLE (accessible), i); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_STRING_AS_STRING, s1, append_basic); + } + else if (strcmp (pname, "accessible-table-caption-object") == 0) + { + otemp = atk_table_get_caption (ATK_TABLE (accessible)); + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + "(so)", otemp, append_object); + } + else + { + emit_event (accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); + } + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +#define STATE_CHANGED "state-changed" + +/* + * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals + * and forwards them as object:state-changed:(param-name) AT-SPI events. Where + * the param-name is part of the ATK state-change signal. + */ +static gboolean +state_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + const gchar *pname; + guint detail1; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + pname = g_value_get_string (¶m_values[1]); + + detail1 = (g_value_get_boolean (¶m_values[2])) ? 1 : 0; + emit_event (accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); + + if (!g_strcmp0 (pname, "defunct") && detail1) + spi_register_deregister_object (spi_global_register, G_OBJECT (accessible), + TRUE); + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * The window event listener handles the following ATK signals and forwards + * them as AT-SPI events: + * + * window:create -> window:create + * window:destroy -> window:destroy + * window:minimize -> window:minimize + * window:maximize -> window:maximize + * window:activate -> window:activate + * window:deactivate -> window:deactivate + */ +static gboolean +window_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + GSignalQuery signal_query; + const gchar *name, *s; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + s = atk_object_get_name (accessible); + emit_event (accessible, ITF_EVENT_WINDOW, name, "", 0, 0, + DBUS_TYPE_STRING_AS_STRING, s, append_basic); + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * The document event listener handles the following ATK signals + * and converts them to AT-SPI events: + * + * Gtk:AtkDocument:load-complete -> document:load-complete + * Gtk:AtkDocument:load-stopped -> document:load-stopped + * Gtk:AtkDocument:reload -> document:reload + * Gtk:AtkDocument:page-changed -> document:page-changed + */ +static gboolean +document_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + GSignalQuery signal_query; + const gchar *name, *s; + gint detail1 = 0; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + if (n_param_values > 0) // on the case of page-changed + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + s = atk_object_get_name (accessible); + emit_event (accessible, ITF_EVENT_DOCUMENT, name, "", detail1, 0, + DBUS_TYPE_STRING_AS_STRING, s, append_basic); + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Signal handler for "Gtk:AtkComponent:bounds-changed". Converts + * this to an AT-SPI event - "object:bounds-changed". + */ +static gboolean +bounds_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + AtkRectangle *atk_rect; + GSignalQuery signal_query; + const gchar *name; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + + if (G_VALUE_HOLDS_BOXED (param_values + 1)) + { + atk_rect = g_value_get_boxed (param_values + 1); + + emit_event (accessible, ITF_EVENT_OBJECT, name, "", 0, 0, + "(iiii)", atk_rect, append_rect); + } + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and + * converts it to the AT-SPI signal - 'object:active-descendant-changed'. + * + */ +static gboolean +active_descendant_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + AtkObject *child; + GSignalQuery signal_query; + const gchar *name; + gint detail1; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + child = ATK_OBJECT (g_value_get_pointer (¶m_values[1])); + g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE); + + detail1 = atk_object_get_index_in_parent (child); + + emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, + "(so)", child, append_object); + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and + * converts it to the AT-SPI signal - 'object:link-selected' + * + */ +static gboolean +link_selected_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + GSignalQuery signal_query; + const gchar *name, *minor; + gint detail1 = 0; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + minor = g_quark_to_string (signal_hint->detail); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Handles the ATK signal 'Gtk:AtkText:text-changed' and + * converts it to the AT-SPI signal - 'object:text-changed' + * This signal is deprecated by Gtk:AtkText:text-insert + * and Gtk:AtkText:text-remove + * + */ +static gboolean +text_changed_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + GSignalQuery signal_query; + const gchar *name, *minor; + gchar *selected; + gint detail1 = 0, detail2 = 0; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + minor = g_quark_to_string (signal_hint->detail); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + selected = + atk_text_get_text (ATK_TEXT (accessible), detail1, detail1 + detail2); + + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, selected, append_basic); + g_free (selected); + + return TRUE; +} + +/* + * Handles the ATK signal 'Gtk:AtkText:text-insert' and + * converts it to the AT-SPI signal - 'object:text-changed' + * + */ +static gboolean +text_insert_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + guint text_changed_signal_id; + GSignalQuery signal_query; + const gchar *name; + const gchar *minor_raw, *text = NULL; + gchar *minor; + gint detail1 = 0, detail2 = 0; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + /* Get signal name for 'Gtk:AtkText:text-changed' so + * we convert it to the AT-SPI signal - 'object:text-changed' + */ + text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible)); + g_signal_query (text_changed_signal_id, &signal_query); + name = signal_query.signal_name; + + + /* Add the insert and keep any detail coming from atk */ + minor_raw = g_quark_to_string (signal_hint->detail); + if (minor_raw) + minor = g_strconcat ("insert:", minor_raw, NULL); + else + minor = g_strdup ("insert"); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + if (G_VALUE_TYPE (¶m_values[3]) == G_TYPE_STRING) + text = g_value_get_string (¶m_values[3]); + + if (text != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, text, append_basic); + g_free (minor); + return TRUE; +} + +/* + * Handles the ATK signal 'Gtk:AtkText:text-remove' and + * converts it to the AT-SPI signal - 'object:text-changed' + * + */ +static gboolean +text_remove_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + guint text_changed_signal_id; + GSignalQuery signal_query; + const gchar *name; + const gchar *minor_raw, *text = NULL; + gchar *minor; + gint detail1 = 0, detail2 = 0; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + /* Get signal name for 'Gtk:AtkText:text-changed' so + * we convert it to the AT-SPI signal - 'object:text-changed' + */ + text_changed_signal_id = g_signal_lookup ("text-changed", G_OBJECT_TYPE (accessible)); + g_signal_query (text_changed_signal_id, &signal_query); + name = signal_query.signal_name; + + minor_raw = g_quark_to_string (signal_hint->detail); + + /* Add the delete and keep any detail coming from atk */ + if (minor_raw) + minor = g_strconcat ("delete:", minor_raw, NULL); + else + minor = g_strdup ("delete"); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + if (G_VALUE_TYPE (¶m_values[3]) == G_TYPE_STRING) + text = g_value_get_string (¶m_values[3]); + + if (text != NULL) + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, text, append_basic); + g_free (minor); + return TRUE; +} + + +/*---------------------------------------------------------------------------*/ + +/* + * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and + * converts it to the AT-SPI signal - 'object:text-selection-changed' + * + */ +static gboolean +text_selection_changed_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, + gpointer data) +{ + AtkObject *accessible; + GSignalQuery signal_query; + const gchar *name, *minor; + gint detail1 = 0, detail2 = 0; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + minor = g_quark_to_string (signal_hint->detail); + + if (G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + DBUS_TYPE_STRING_AS_STRING, "", append_basic); + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Children changed signal converter and forwarder. + * + * Klass (Interface) org.a11y.atspi.Event.Object + * Major is the signal name. + * Minor is 'add' or 'remove' + * detail1 is the index. + * detail2 is 0. + * any_data is the child reference. + */ +static gboolean +children_changed_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + GSignalQuery signal_query; + const gchar *name, *minor; + gint detail1 = 0, detail2 = 0; + + AtkObject *accessible, *ao=NULL; + gpointer child; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + minor = g_quark_to_string (signal_hint->detail); + + detail1 = g_value_get_uint (param_values + 1); + child = g_value_get_pointer (param_values + 2); + + if (ATK_IS_OBJECT (child)) + { + ao = ATK_OBJECT (child); + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + "(so)", ao, append_object); + } + else if ((minor != NULL) && (strcmp (minor, "add") == 0)) + { + ao = atk_object_ref_accessible_child (accessible, + detail1); + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + "(so)", ao, append_object); + g_object_unref (ao); + } + else + { + emit_event (accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, + "(so)", ao, append_object); + } + + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Generic signal converter and forwarder. + * + * Klass (Interface) org.a11y.atspi.Event.Object + * Major is the signal name. + * Minor is NULL. + * detail1 is 0. + * detail2 is 0. + * any_data is NULL. + */ +static gboolean +generic_event_listener (GSignalInvocationHint * signal_hint, + guint n_param_values, + const GValue * param_values, gpointer data) +{ + AtkObject *accessible; + GSignalQuery signal_query; + const gchar *name; + int detail1 = 0, detail2 = 0; + + g_signal_query (signal_hint->signal_id, &signal_query); + name = signal_query.signal_name; + + accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); + + if (n_param_values > 1 && G_VALUE_TYPE (¶m_values[1]) == G_TYPE_INT) + detail1 = g_value_get_int (¶m_values[1]); + + if (n_param_values > 2 && G_VALUE_TYPE (¶m_values[2]) == G_TYPE_INT) + detail2 = g_value_get_int (¶m_values[2]); + + emit_event (accessible, ITF_EVENT_OBJECT, name, "", detail1, detail2, + DBUS_TYPE_INT32_AS_STRING, 0, append_basic); + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +/* + * Registers the provided function as a handler for the given signal name + * and stores the signal id returned so that the function may be + * de-registered later. + */ +static guint +add_signal_listener (GSignalEmissionHook listener, const char *signal_name) +{ + guint id; + + id = atk_add_global_event_listener (listener, signal_name); + + if (id > 0) /* id == 0 is a failure */ + g_array_append_val (listener_ids, id); + + return id; +} + +/* + * Initialization for the signal handlers. + * + * Registers all required signal handlers. + */ +void +spi_atk_register_event_listeners (void) +{ + /* + * Kludge to make sure the Atk interface types are registered, otherwise + * the AtkText signal handlers below won't get registered + */ + GObject *ao = g_object_new (ATK_TYPE_OBJECT, NULL); + AtkObject *bo = atk_no_op_object_new (ao); + guint id = 0; + + g_object_unref (G_OBJECT (bo)); + g_object_unref (ao); + + if (listener_ids) + { + g_warning ("atk_bridge: spi_atk-register_event_listeners called multiple times"); + return; + } + + /* Register for focus event notifications, and register app with central registry */ + listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16); + + atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker); + + add_signal_listener (property_event_listener, + "Gtk:AtkObject:property-change"); + + /* window events: we tentative try to register using the old format */ + id = add_signal_listener (window_event_listener, "window:create"); + + if (id != 0) + { + /* If we are able to register using the old format, we assume + * that the ATK implementor is managing window events without + * AtkWindow. We can't use the opposite test because after + * including AtkWindow on ATK you would be able to register to + * that event, although the ATK implementor could or not use it. + */ + + add_signal_listener (window_event_listener, "window:destroy"); + add_signal_listener (window_event_listener, "window:minimize"); + add_signal_listener (window_event_listener, "window:maximize"); + add_signal_listener (window_event_listener, "window:restore"); + add_signal_listener (window_event_listener, "window:activate"); + add_signal_listener (window_event_listener, "window:deactivate"); + } + else + { + add_signal_listener (window_event_listener, "Atk:AtkWindow:create"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:destroy"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:minimize"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:maximize"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:restore"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:activate"); + add_signal_listener (window_event_listener, "Atk:AtkWindow:deactivate"); + } + + add_signal_listener (document_event_listener, + "Gtk:AtkDocument:load-complete"); + add_signal_listener (document_event_listener, "Gtk:AtkDocument:reload"); + add_signal_listener (document_event_listener, + "Gtk:AtkDocument:load-stopped"); + add_signal_listener (document_event_listener, + "Gtk:AtkDocument:page-changed"); + /* TODO Fake this event on the client side */ + add_signal_listener (state_event_listener, "Gtk:AtkObject:state-change"); + /* TODO */ + add_signal_listener (active_descendant_event_listener, + "Gtk:AtkObject:active-descendant-changed"); + add_signal_listener (bounds_event_listener, + "Gtk:AtkComponent:bounds-changed"); + add_signal_listener (text_selection_changed_event_listener, + "Gtk:AtkText:text-selection-changed"); + add_signal_listener (text_changed_event_listener, + "Gtk:AtkText:text-changed"); + add_signal_listener (text_insert_event_listener, + "Gtk:AtkText:text-insert"); + add_signal_listener (text_remove_event_listener, + "Gtk:AtkText:text-remove"); + add_signal_listener (link_selected_event_listener, + "Gtk:AtkHypertext:link-selected"); + add_signal_listener (generic_event_listener, + "Gtk:AtkObject:visible-data-changed"); + add_signal_listener (generic_event_listener, + "Gtk:AtkSelection:selection-changed"); + add_signal_listener (generic_event_listener, + "Gtk:AtkText:text-attributes-changed"); + add_signal_listener (generic_event_listener, + "Gtk:AtkText:text-caret-moved"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-inserted"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-reordered"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:row-deleted"); + add_signal_listener (generic_event_listener, + "Gtk:AtkTable:column-inserted"); + add_signal_listener (generic_event_listener, + "Gtk:AtkTable:column-reordered"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:column-deleted"); + add_signal_listener (generic_event_listener, "Gtk:AtkTable:model-changed"); + add_signal_listener (children_changed_event_listener, "Gtk:AtkObject:children-changed"); + +#if 0 + g_signal_connect (G_OBJECT (spi_global_app_data->root), + "children-changed::add", + (GCallback) toplevel_added_event_listener, NULL); + + g_signal_connect (G_OBJECT (spi_global_app_data->root), + "children-changed::remove", + (GCallback) toplevel_removed_event_listener, NULL); +#endif + + /* + * May add the following listeners to implement preemptive key listening for GTK+ + * + * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event"); + * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event"); + */ + atk_bridge_key_event_listener_id = + atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL); +} + +/*---------------------------------------------------------------------------*/ + +/* + * De-registers all ATK signal handlers. + */ +void +spi_atk_deregister_event_listeners (void) +{ + gint i; + GArray *ids = listener_ids; + listener_ids = NULL; + + if (atk_bridge_focus_tracker_id) + { + atk_remove_focus_tracker (atk_bridge_focus_tracker_id); + atk_bridge_focus_tracker_id = 0; + } + + if (ids) + { + for (i = 0; i < ids->len; i++) + { + atk_remove_global_event_listener (g_array_index (ids, guint, i)); + } + g_array_free (ids, TRUE); + } + + if (atk_bridge_key_event_listener_id) + { + atk_remove_key_event_listener (atk_bridge_key_event_listener_id); + atk_bridge_key_event_listener_id = 0; + } +} + +/*---------------------------------------------------------------------------*/ + +/* + * TODO This function seems out of place here. + * + * Emits fake deactivate signals on all top-level windows. + * Used when shutting down AT-SPI, ensuring that all + * windows have been removed on the client side. + */ +void +spi_atk_tidy_windows (void) +{ + AtkObject *root; + gint n_children; + gint i; + + root = atk_get_root (); + n_children = atk_object_get_n_accessible_children (root); + for (i = 0; i < n_children; i++) + { + AtkObject *child; + AtkStateSet *stateset; + const gchar *name; + + child = atk_object_ref_accessible_child (root, i); + stateset = atk_object_ref_state_set (child); + + name = atk_object_get_name (child); + if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE)) + { + emit_event (child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, + DBUS_TYPE_STRING_AS_STRING, name, append_basic); + } + g_object_unref (stateset); + + emit_event (child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, + DBUS_TYPE_STRING_AS_STRING, name, append_basic); + g_object_unref (child); + } +} + +gboolean +spi_event_is_subtype (gchar **needle, gchar **haystack) +{ + while (*haystack && **haystack) + { + if (g_strcmp0 (*needle, *haystack)) + return FALSE; + needle++; + haystack++; + } + return TRUE; +} + +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/event.h b/atk-adaptor/event.h new file mode 100644 index 0000000..1a9f9f3 --- /dev/null +++ b/atk-adaptor/event.h @@ -0,0 +1,33 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * Copyright 2008, 2009 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef EVENT_H +#define EVENT_H + +void spi_atk_register_event_listeners (void); +void spi_atk_deregister_event_listeners (void); +void spi_atk_tidy_windows (void); + +gboolean spi_event_is_subtype (gchar **needle, gchar **haystack); +#endif /* EVENT_H */ diff --git a/atk-adaptor/gtk-2.0/Makefile.am b/atk-adaptor/gtk-2.0/Makefile.am new file mode 100644 index 0000000..4aac9c3 --- /dev/null +++ b/atk-adaptor/gtk-2.0/Makefile.am @@ -0,0 +1,5 @@ + gtkmoduledir = $(libdir)/gtk-2.0/modules + +include $(top_srcdir)/atk-adaptor/Makefile.include + +libatk_bridge_la_SOURCES = module.c diff --git a/atk-adaptor/gtk-2.0/module.c b/atk-adaptor/gtk-2.0/module.c new file mode 100644 index 0000000..d2a49ee --- /dev/null +++ b/atk-adaptor/gtk-2.0/module.c @@ -0,0 +1,62 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008, 2009 Codethink Ltd. + * Copyright 2001, 2002, 2003 Sun Microsystems Inc., + * Copyright 2001, 2002, 2003 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include "config.h" + +#include <gmodule.h> +#include <atk-bridge.h> + +/*---------------------------------------------------------------------------*/ + +int +gtk_module_init (gint * argc, gchar ** argv[]) +{ + return atk_bridge_adaptor_init (argc, argv); +} + +gchar* +g_module_check_init (GModule *module) +{ + g_module_make_resident (module); + + return NULL; +} + +void +gnome_accessibility_module_init (void) +{ + atk_bridge_adaptor_init (NULL, NULL); + + if (g_getenv ("AT_SPI_DEBUG")) + { + g_print ("Atk Accessibility bridge initialized\n"); + } +} + +void +gnome_accessibility_module_shutdown (void) +{ + atk_bridge_adaptor_cleanup (); +} diff --git a/atk-adaptor/introspection.c b/atk-adaptor/introspection.c new file mode 100644 index 0000000..bc1f332 --- /dev/null +++ b/atk-adaptor/introspection.c @@ -0,0 +1,913 @@ + +/* + * This file has been auto-generated from the introspection data available + * in the at-spi2-core repository. The D-Bus procol is defined in this + * repository, which can be found at: + * + * http://download.gnome.org/sources/at-spi2-core/0.1/ + * + * DO NOT EDIT. + */ + + +const char *spi_org_a11y_atspi_Accessible = +"<interface name=\"org.a11y.atspi.Accessible\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"Name\" type=\"s\" />" +"" +" <property access=\"read\" name=\"Description\" type=\"s\" />" +"" +" <property access=\"read\" name=\"Parent\" type=\"(so)\">" +" " +" </property>" +"" +" <property access=\"read\" name=\"ChildCount\" type=\"i\" />" +"" +" <property access=\"read\" name=\"Locale\" type=\"s\" />" +"" +" <method name=\"GetChildAtIndex\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetChildren\">" +" <arg direction=\"out\" type=\"a(so)\" />" +" " +" </method>" +"" +" <method name=\"GetIndexInParent\">" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetRelationSet\">" +" <arg direction=\"out\" type=\"a(ua(so))\" />" +" " +" </method>" +"" +" <method name=\"GetRole\">" +" <arg direction=\"out\" type=\"u\" />" +" </method>" +"" +" <method name=\"GetRoleName\">" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetLocalizedRoleName\">" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetState\">" +" <arg direction=\"out\" type=\"au\" />" +" " +" </method>" +"" +" <method name=\"GetAttributes\">" +" <arg direction=\"out\" type=\"a{ss}\" />" +" " +" </method>" +"" +" <method name=\"GetApplication\">" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Action = +"<interface name=\"org.a11y.atspi.Action\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"NActions\" type=\"i\" />" +"" +" <method name=\"GetDescription\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetName\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetLocalizedName\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetKeyBinding\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetActions\">" +" <arg direction=\"out\" type=\"a(sss)\" />" +" " +" </method>" +"" +" <method name=\"DoAction\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Application = +"<interface name=\"org.a11y.atspi.Application\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"ToolkitName\" type=\"s\" />" +"" +" <property access=\"read\" name=\"Version\" type=\"s\" />" +"" +" <property access=\"read\" name=\"AtspiVersion\" type=\"s\" />" +" <property access=\"read\" name=\"Id\" type=\"i\" />" +"" +" <method name=\"GetLocale\">" +" <arg direction=\"in\" name=\"lctype\" type=\"u\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"RegisterEventListener\">" +" <arg direction=\"in\" name=\"event\" type=\"s\" />" +" </method>" +"" +" <method name=\"DeregisterEventListener\">" +" <arg direction=\"in\" name=\"event\" type=\"s\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Collection = +"<interface name=\"org.a11y.atspi.Collection\" version=\"0.1.7\">" +"" +" <method name=\"GetMatches\">" +" <arg direction=\"in\" name=\"rule\" type=\"(auuasuauusub)\" />" +" " +" <arg direction=\"in\" name=\"sortby\" type=\"u\" />" +" <arg direction=\"in\" name=\"count\" type=\"i\" />" +" <arg direction=\"in\" name=\"traverse\" type=\"b\" />" +" <arg direction=\"out\" type=\"a(so)\" />" +" " +" </method>" +"" +" <method name=\"GetMatchesTo\">" +" <arg direction=\"in\" name=\"current_object\" type=\"o\" />" +" " +" <arg direction=\"in\" name=\"rule\" type=\"(auuasuauusub)\" />" +" " +" <arg direction=\"in\" name=\"sortby\" type=\"u\" />" +" <arg direction=\"in\" name=\"tree\" type=\"u\" />" +" <arg direction=\"in\" name=\"limit_scope\" type=\"b\" />" +" <arg direction=\"in\" name=\"count\" type=\"i\" />" +" <arg direction=\"in\" name=\"traverse\" type=\"b\" />" +" <arg direction=\"out\" type=\"a(so)\" />" +" " +" </method>" +"" +" <method name=\"GetMatchesFrom\">" +" <arg direction=\"in\" name=\"current_object\" type=\"o\" />" +" " +" <arg direction=\"in\" name=\"rule\" type=\"(auuasuauusub)\" />" +" " +" <arg direction=\"in\" name=\"sortby\" type=\"u\" />" +" <arg direction=\"in\" name=\"tree\" type=\"u\" />" +" <arg direction=\"in\" name=\"count\" type=\"i\" />" +" <arg direction=\"in\" name=\"traverse\" type=\"b\" />" +" <arg direction=\"out\" type=\"a(so)\" />" +" " +" </method>" +"" +" <method name=\"GetActiveDescendant\">" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Component = +"<interface name=\"org.a11y.atspi.Component\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"HighlightIndex\" type=\"i\" />" +"" +" <method name=\"Contains\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"coord_type\" type=\"u\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"GetAccessibleAtPoint\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"coord_type\" type=\"u\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetExtents\">" +" <arg direction=\"in\" name=\"coord_type\" type=\"u\" />" +" <arg direction=\"out\" type=\"(iiii)\" />" +" " +" </method>" +"" +" <method name=\"GetPosition\">" +" <arg direction=\"in\" name=\"coord_type\" type=\"u\" />" +" <arg direction=\"out\" name=\"x\" type=\"i\" />" +" <arg direction=\"out\" name=\"y\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetSize\">" +" <arg direction=\"out\" name=\"width\" type=\"i\" />" +" <arg direction=\"out\" name=\"height\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetLayer\">" +" <arg direction=\"out\" type=\"u\" />" +" </method>" +"" +" <method name=\"GetMDIZOrder\">" +" <arg direction=\"out\" type=\"n\" />" +" </method>" +"" +" <method name=\"GrabFocus\">" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"GrabHighlight\">" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"ClearHighlight\">" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"GetAlpha\">" +" <arg direction=\"out\" type=\"d\" />" +" </method>" +"" +" <method name=\"SetExtents\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"width\" type=\"i\" />" +" <arg direction=\"in\" name=\"height\" type=\"i\" />" +" <arg direction=\"in\" name=\"coord_type\" type=\"u\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"SetPosition\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"coord_type\" type=\"u\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"SetSize\">" +" <arg direction=\"in\" name=\"width\" type=\"i\" />" +" <arg direction=\"in\" name=\"height\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Document = +"<interface name=\"org.a11y.atspi.Document\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"CurrentPageNumber\" type=\"i\" />" +"" +" <property access=\"read\" name=\"PageCount\" type=\"i\" />" +"" +" <method name=\"GetLocale\">" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetAttributeValue\">" +" <arg direction=\"in\" name=\"attributename\" type=\"s\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetAttributes\">" +" <arg direction=\"out\" type=\"{ss}\" />" +" " +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Hypertext = +"<interface name=\"org.a11y.atspi.Hypertext\" version=\"0.1.7\">" +"" +" <method name=\"GetNLinks\">" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetLink\">" +" <arg direction=\"in\" name=\"linkIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetLinkIndex\">" +" <arg direction=\"in\" name=\"characterIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Hyperlink = +"<interface name=\"org.a11y.atspi.Hyperlink\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"NAnchors\" type=\"n\" />" +"" +" <property access=\"read\" name=\"StartIndex\" type=\"i\" />" +"" +" <property access=\"read\" name=\"EndIndex\" type=\"i\" />" +"" +" <method name=\"GetObject\">" +" <arg direction=\"in\" name=\"i\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetURI\">" +" <arg direction=\"in\" name=\"i\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"IsValid\">" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Image = +"<interface name=\"org.a11y.atspi.Image\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"ImageDescription\" type=\"s\" />" +"" +" <property access=\"read\" name=\"ImageLocale\" type=\"s\" />" +"" +" <method name=\"GetImageExtents\">" +" <arg direction=\"in\" name=\"coordType\" type=\"u\" />" +" <arg direction=\"out\" type=\"(iiii)\" />" +" " +" </method>" +"" +" <method name=\"GetImagePosition\">" +" <arg direction=\"out\" name=\"x\" type=\"i\" />" +" <arg direction=\"out\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"coordType\" type=\"u\" />" +" </method>" +"" +" <method name=\"GetImageSize\">" +" <arg direction=\"out\" name=\"width\" type=\"i\" />" +" <arg direction=\"out\" name=\"height\" type=\"i\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Selection = +"<interface name=\"org.a11y.atspi.Selection\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"NSelectedChildren\" type=\"i\" />" +"" +" <method name=\"GetSelectedChild\">" +" <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"SelectChild\">" +" <arg direction=\"in\" name=\"childIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"DeselectSelectedChild\">" +" <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"IsChildSelected\">" +" <arg direction=\"in\" name=\"childIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"SelectAll\">" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"ClearSelection\">" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"DeselectChild\">" +" <arg direction=\"in\" name=\"childIndex\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Table = +"<interface name=\"org.a11y.atspi.Table\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"NRows\" type=\"i\" />" +"" +" <property access=\"read\" name=\"NColumns\" type=\"i\" />" +"" +" <property access=\"read\" name=\"Caption\" type=\"(so)\">" +" " +" </property>" +"" +" <property access=\"read\" name=\"Summary\" type=\"(so)\">" +" " +" </property>" +"" +" <property access=\"read\" name=\"NSelectedRows\" type=\"i\" />" +"" +" <property access=\"read\" name=\"NSelectedColumns\" type=\"i\" />" +"" +" <method name=\"GetAccessibleAt\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetIndexAt\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetRowAtIndex\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetColumnAtIndex\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetRowDescription\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetColumnDescription\">" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"GetRowExtentAt\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetColumnExtentAt\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetRowHeader\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetColumnHeader\">" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"(so)\" />" +" " +" </method>" +"" +" <method name=\"GetSelectedRows\">" +" <arg direction=\"out\" type=\"ai\" />" +" " +" </method>" +"" +" <method name=\"GetSelectedColumns\">" +" <arg direction=\"out\" type=\"ai\" />" +" " +" </method>" +"" +" <method name=\"IsRowSelected\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"IsColumnSelected\">" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"IsSelected\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"AddRowSelection\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"AddColumnSelection\">" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"RemoveRowSelection\">" +" <arg direction=\"in\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"RemoveColumnSelection\">" +" <arg direction=\"in\" name=\"column\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"GetRowColumnExtentsAtIndex\">" +" <arg direction=\"in\" name=\"index\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" <arg direction=\"out\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" name=\"col\" type=\"i\" />" +" <arg direction=\"out\" name=\"row_extents\" type=\"i\" />" +" <arg direction=\"out\" name=\"col_extents\" type=\"i\" />" +" <arg direction=\"out\" name=\"is_selected\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_TableCell = +"<interface name=\"org.a11y.atspi.TableCell\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"ColumnSpan\" type=\"i\" />" +"" +" <property access=\"read\" name=\"Position\" type=\"(ii)\" />" +"" +" <property access=\"read\" name=\"RowSpan\" type=\"i\" />" +"" +" <property access=\"read\" name=\"Table\" type=\"(so)\" />" +"" +" <method name=\"GetRowColumnSpan\">" +" <arg direction=\"out\" type=\"b\" />" +" <arg direction=\"out\" name=\"row\" type=\"i\" />" +" <arg direction=\"out\" name=\"col\" type=\"i\" />" +" <arg direction=\"out\" name=\"row_extents\" type=\"i\" />" +" <arg direction=\"out\" name=\"col_extents\" type=\"i\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Text = +"<interface name=\"org.a11y.atspi.Text\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"CharacterCount\" type=\"i\" />" +"" +" <property access=\"read\" name=\"CaretOffset\" type=\"i\" />" +"" +" <method name=\"GetStringAtOffset\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"in\" name=\"granularity\" type=\"u\" />" +" <arg direction=\"out\" type=\"s\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetText\">" +" <arg direction=\"in\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"in\" name=\"endOffset\" type=\"i\" />" +" <arg direction=\"out\" type=\"s\" />" +" </method>" +"" +" <method name=\"SetCaretOffset\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"GetTextBeforeOffset\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"in\" name=\"type\" type=\"u\" />" +" <arg direction=\"out\" type=\"s\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetTextAtOffset\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"in\" name=\"type\" type=\"u\" />" +" <arg direction=\"out\" type=\"s\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetTextAfterOffset\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"in\" name=\"type\" type=\"u\" />" +" <arg direction=\"out\" type=\"s\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetCharacterAtOffset\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetAttributeValue\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"in\" name=\"attributeName\" type=\"s\" />" +" <arg direction=\"out\" type=\"s\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"defined\" type=\"b\" />" +" </method>" +"" +" <method name=\"GetAttributes\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"out\" type=\"a{ss}\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" " +" </method>" +"" +" <method name=\"GetDefaultAttributes\">" +" <arg direction=\"out\" type=\"a{ss}\" />" +" " +" </method>" +"" +" <method name=\"GetCharacterExtents\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"out\" name=\"x\" type=\"i\" />" +" <arg direction=\"out\" name=\"y\" type=\"i\" />" +" <arg direction=\"out\" name=\"width\" type=\"i\" />" +" <arg direction=\"out\" name=\"height\" type=\"i\" />" +" <arg direction=\"in\" name=\"coordType\" type=\"u\" />" +" </method>" +"" +" <method name=\"GetOffsetAtPoint\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"coordType\" type=\"u\" />" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetNSelections\">" +" <arg direction=\"out\" type=\"i\" />" +" </method>" +"" +" <method name=\"GetSelection\">" +" <arg direction=\"in\" name=\"selectionNum\" type=\"i\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" </method>" +"" +" <method name=\"AddSelection\">" +" <arg direction=\"in\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"in\" name=\"endOffset\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"RemoveSelection\">" +" <arg direction=\"in\" name=\"selectionNum\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"SetSelection\">" +" <arg direction=\"in\" name=\"selectionNum\" type=\"i\" />" +" <arg direction=\"in\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"in\" name=\"endOffset\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"GetRangeExtents\">" +" <arg direction=\"in\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"in\" name=\"endOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"x\" type=\"i\" />" +" <arg direction=\"out\" name=\"y\" type=\"i\" />" +" <arg direction=\"out\" name=\"width\" type=\"i\" />" +" <arg direction=\"out\" name=\"height\" type=\"i\" />" +" <arg direction=\"in\" name=\"coordType\" type=\"u\" />" +" </method>" +"" +" <method name=\"GetBoundedRanges\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"width\" type=\"i\" />" +" <arg direction=\"in\" name=\"height\" type=\"i\" />" +" <arg direction=\"in\" name=\"coordType\" type=\"u\" />" +" <arg direction=\"in\" name=\"xClipType\" type=\"u\" />" +" <arg direction=\"in\" name=\"yClipType\" type=\"u\" />" +" <arg direction=\"out\" type=\"a(iisv)\" />" +" " +" </method>" +"" +" <method name=\"GetAttributeRun\">" +" <arg direction=\"in\" name=\"offset\" type=\"i\" />" +" <arg direction=\"in\" name=\"includeDefaults\" type=\"b\" />" +" <arg direction=\"out\" type=\"a{ss}\" />" +" <arg direction=\"out\" name=\"startOffset\" type=\"i\" />" +" <arg direction=\"out\" name=\"endOffset\" type=\"i\" />" +" " +" </method>" +"" +" <method name=\"GetDefaultAttributeSet\">" +" <arg direction=\"out\" type=\"a{ss}\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_EditableText = +"<interface name=\"org.a11y.atspi.EditableText\" version=\"0.1.7\">" +"" +" <method name=\"SetTextContents\">" +" <arg direction=\"in\" name=\"newContents\" type=\"s\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"InsertText\">" +" <arg direction=\"in\" name=\"position\" type=\"i\" />" +" <arg direction=\"in\" name=\"text\" type=\"s\" />" +" <arg direction=\"in\" name=\"length\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"CopyText\">" +" <arg direction=\"in\" name=\"startPos\" type=\"i\" />" +" <arg direction=\"in\" name=\"endPos\" type=\"i\" />" +" </method>" +"" +" <method name=\"CutText\">" +" <arg direction=\"in\" name=\"startPos\" type=\"i\" />" +" <arg direction=\"in\" name=\"endPos\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"DeleteText\">" +" <arg direction=\"in\" name=\"startPos\" type=\"i\" />" +" <arg direction=\"in\" name=\"endPos\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"PasteText\">" +" <arg direction=\"in\" name=\"position\" type=\"i\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Cache = +"<interface name=\"org.a11y.atspi.Cache\" version=\"0.1.7\">" +"" +" <method name=\"GetItems\">" +" <arg direction=\"out\" name=\"nodes\" type=\"a((so)(so)a(so)assusau)\" />" +" " +" </method>" +"" +" <signal name=\"AddAccessible\">" +" <arg name=\"nodeAdded\" type=\"((so)(so)a(so)assusau)\" />" +" " +" </signal>" +"" +" <signal name=\"RemoveAccessible\">" +" <arg name=\"nodeRemoved\" type=\"(so)\" />" +" " +" </signal>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Value = +"<interface name=\"org.a11y.atspi.Value\" version=\"0.1.7\">" +"" +" <property access=\"read\" name=\"MinimumValue\" type=\"d\" />" +"" +" <property access=\"read\" name=\"MaximumValue\" type=\"d\" />" +"" +" <property access=\"read\" name=\"MinimumIncrement\" type=\"d\" />" +"" +" <property access=\"readwrite\" name=\"CurrentValue\" type=\"d\" />" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_Registry = +"<interface name=\"org.a11y.atspi.Registry\" version=\"0.1.7\">" +"" +" <method name=\"RegisterEvent\">" +" <arg direction=\"in\" name=\"event\" type=\"s\">" +" </arg>" +" </method>" +"" +" <method name=\"DeregisterEvent\">" +" <arg direction=\"in\" name=\"event\" type=\"s\">" +" </arg>" +" </method>" +"" +" <method name=\"GetRegisteredEvents\">" +" <arg direction=\"out\" name=\"events\" type=\"a(ss)\">" +" </arg>" +" </method>" +"" +" <signal name=\"EventListenerRegistered\">" +" <arg direction=\"out\" name=\"bus\" type=\"s\" />" +" <arg direction=\"out\" name=\"path\" type=\"s\" />" +" </signal>" +"" +" <signal name=\"EventListenerDeregistered\">" +" <arg direction=\"out\" name=\"bus\" type=\"s\" />" +" <arg direction=\"out\" name=\"path\" type=\"s\" />" +" </signal>" +"</interface>" +""; + +const char *spi_org_a11y_atspi_DeviceEventController = +"<interface name=\"org.a11y.atspi.DeviceEventController\" version=\"0.1.7\">" +"" +" <method name=\"RegisterKeystrokeListener\">" +" <arg direction=\"in\" name=\"listener\" type=\"o\" />" +" <arg direction=\"in\" name=\"keys\" type=\"a(iisi)\">" +" " +" </arg>" +" <arg direction=\"in\" name=\"mask\" type=\"u\" />" +" <arg direction=\"in\" name=\"type\" type=\"au\">" +" " +" </arg>" +" <arg direction=\"in\" name=\"mode\" type=\"(bbb)\">" +" " +" </arg>" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"DeregisterKeystrokeListener\">" +" <arg direction=\"in\" name=\"listener\" type=\"o\" />" +" <arg direction=\"in\" name=\"keys\" type=\"a(iisi)\">" +" " +" </arg>" +" <arg direction=\"in\" name=\"mask\" type=\"u\" />" +" <arg direction=\"in\" name=\"type\" type=\"u\" />" +" </method>" +"" +" <method name=\"RegisterDeviceEventListener\">" +" <arg direction=\"in\" name=\"listener\" type=\"o\" />" +" <arg direction=\"in\" name=\"types\" type=\"u\" />" +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +" <method name=\"DeregisterDeviceEventListener\">" +" <arg direction=\"in\" name=\"listener\" type=\"o\" />" +" <arg direction=\"in\" name=\"types\" type=\"u\" />" +" </method>" +"" +" <method name=\"GenerateKeyboardEvent\">" +" <arg direction=\"in\" name=\"keycode\" type=\"i\" />" +" <arg direction=\"in\" name=\"keystring\" type=\"s\" />" +" <arg direction=\"in\" name=\"type\" type=\"u\" />" +" </method>" +"" +" <method name=\"GenerateMouseEvent\">" +" <arg direction=\"in\" name=\"x\" type=\"i\" />" +" <arg direction=\"in\" name=\"y\" type=\"i\" />" +" <arg direction=\"in\" name=\"eventName\" type=\"s\" />" +" </method>" +"" +" <method name=\"NotifyListenersSync\">" +" <arg direction=\"in\" name=\"event\" type=\"(uiuuisb)\" />" +" <arg direction=\"out\" type=\"b\" />" +" " +" </method>" +"" +" <method name=\"NotifyListenersAsync\">" +" <arg direction=\"in\" name=\"event\" type=\"(uiuuisb)\" />" +" " +" </method>" +"" +"</interface>" +""; + +const char *spi_org_a11y_atspi_DeviceEventListener = +"<interface name=\"org.a11y.atspi.DeviceEventListener\" version=\"0.1.7\">" +"" +" <method name=\"NotifyEvent\">" +" <arg direction=\"in\" name=\"event\" type=\"(uiuuisb)\" />" +" " +" <arg direction=\"out\" type=\"b\" />" +" </method>" +"" +"</interface>" +""; + diff --git a/atk-adaptor/introspection.h b/atk-adaptor/introspection.h new file mode 100644 index 0000000..2210483 --- /dev/null +++ b/atk-adaptor/introspection.h @@ -0,0 +1,55 @@ + +/* + * This file has been auto-generated from the introspection data available + * in the at-spi2-core repository. The D-Bus procol is defined in this + * repository, which can be found at: + * + * http://download.gnome.org/sources/at-spi2-core/0.1/ + * + * DO NOT EDIT. + */ + +#ifndef SPI_INTROSPECTION_DATA_H_ +#define SPI_INTROSPECTION_DATA_H_ + + +extern const char *spi_org_a11y_atspi_Accessible; + +extern const char *spi_org_a11y_atspi_Action; + +extern const char *spi_org_a11y_atspi_Application; + +extern const char *spi_org_a11y_atspi_Collection; + +extern const char *spi_org_a11y_atspi_Component; + +extern const char *spi_org_a11y_atspi_Document; + +extern const char *spi_org_a11y_atspi_Hypertext; + +extern const char *spi_org_a11y_atspi_Hyperlink; + +extern const char *spi_org_a11y_atspi_Image; + +extern const char *spi_org_a11y_atspi_Selection; + +extern const char *spi_org_a11y_atspi_Table; + +extern const char *spi_org_a11y_atspi_TableCell; + +extern const char *spi_org_a11y_atspi_Text; + +extern const char *spi_org_a11y_atspi_EditableText; + +extern const char *spi_org_a11y_atspi_Cache; + +extern const char *spi_org_a11y_atspi_Value; + +extern const char *spi_org_a11y_atspi_Registry; + +extern const char *spi_org_a11y_atspi_DeviceEventController; + +extern const char *spi_org_a11y_atspi_DeviceEventListener; + + +#endif /* SPI_INTROSPECTION_DATA_H_ */ diff --git a/atk-adaptor/object.c b/atk-adaptor/object.c new file mode 100644 index 0000000..404fb24 --- /dev/null +++ b/atk-adaptor/object.c @@ -0,0 +1,504 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008, 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * This module contains utility functions for exporting AT-SPI + * objects based upon an ATK object. + * + * It incudes functions for marshalling object references + * and supported interfaces to a D-Bus message. + */ + +#include <atk/atk.h> +#include "atspi/atspi.h" +#include "spi-dbus.h" + +#include "accessible-register.h" +#include "accessible-cache.h" +#include "accessible-leasing.h" + +#include "bridge.h" + +/*---------------------------------------------------------------------------*/ + +/* + * This is the all important function that decides whether an object should + * be leased or not. + * + * The choice of algorithm for this is somewhat vuage. We want ideally to lease + * all atk objects that are not owned by their parent. + * + * The 'cache' object attempts to cache all objects that are owned by their + * parent by traversing the tree of accessibles, ignoring the children of + * manages-descendants and transient objects. + * + * This function will simply look for all the accessibles that the cache object + * has not found and assume that they need to be leased. + */ +void +spi_object_lease_if_needed (GObject *obj) +{ + if (!spi_cache_in (spi_global_cache, obj)) + { + spi_leasing_take (spi_global_leasing, obj); + } +} + +/*---------------------------------------------------------------------------*/ + +/* + * It is assumed that all of these functions are returning an accessible + * object to the client side. + * + * All of them will lease the AtkObject if it is deemed neccessary. + */ + +void +spi_object_append_null_reference (DBusMessageIter * iter) +{ + DBusMessageIter iter_struct; + const char *name; + const char *path = ATSPI_DBUS_PATH_NULL; + + name = dbus_bus_get_unique_name (spi_global_app_data->bus); + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, + &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_close_container (iter, &iter_struct); +} + +void +spi_object_append_reference (DBusMessageIter * iter, AtkObject * obj) +{ + DBusMessageIter iter_struct; + const gchar *name; + gchar *path; + + if (!obj) { + spi_object_append_null_reference (iter); + return; + } + + spi_object_lease_if_needed (G_OBJECT (obj)); + + name = dbus_bus_get_unique_name (spi_global_app_data->bus); + path = spi_register_object_to_path (spi_global_register, G_OBJECT (obj)); + + if (!path) + path = g_strdup (SPI_DBUS_PATH_NULL); + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, + &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_close_container (iter, &iter_struct); + + g_free (path); +} + +/* TODO: Perhaps combine with spi_object_append_reference. Leaving separate + * for now in case we want to use a different path for hyperlinks. */ +void +spi_hyperlink_append_reference (DBusMessageIter * iter, AtkHyperlink * obj) +{ + DBusMessageIter iter_struct; + const gchar *name; + gchar *path; + + if (!obj) { + spi_object_append_null_reference (iter); + return; + } + + spi_object_lease_if_needed (G_OBJECT (obj)); + + name = dbus_bus_get_unique_name (spi_global_app_data->bus); + path = spi_register_object_to_path (spi_global_register, G_OBJECT (obj)); + + if (!path) + path = g_strdup (SPI_DBUS_PATH_NULL); + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, + &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_close_container (iter, &iter_struct); + + g_free (path); +} + +void +spi_object_append_v_reference (DBusMessageIter * iter, AtkObject * obj) +{ + DBusMessageIter iter_variant; + + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(so)", + &iter_variant); + spi_object_append_reference (&iter_variant, obj); + dbus_message_iter_close_container (iter, &iter_variant); +} + +void +spi_object_append_desktop_reference (DBusMessageIter * iter) +{ + DBusMessageIter iter_struct; + const char *name = spi_global_app_data->desktop_name; + const char *path = spi_global_app_data->desktop_path; + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, + &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_close_container (iter, &iter_struct); +} + +DBusMessage * +spi_object_return_reference (DBusMessage * msg, AtkObject * obj) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return (msg); + if (reply) + { + DBusMessageIter iter; + dbus_message_iter_init_append (reply, &iter); + spi_object_append_reference (&iter, obj); + } + + return reply; +} + +DBusMessage * +spi_hyperlink_return_reference (DBusMessage * msg, AtkHyperlink * obj) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return (msg); + if (reply) + { + DBusMessageIter iter; + dbus_message_iter_init_append (reply, &iter); + spi_hyperlink_append_reference (&iter, obj); + } + if (obj) + g_object_unref (G_OBJECT (obj)); + + return reply; +} + +/*---------------------------------------------------------------------------*/ + +void +spi_object_append_interfaces (DBusMessageIter * iter, AtkObject * obj) +{ + const gchar *itf; + + itf = ATSPI_DBUS_INTERFACE_ACCESSIBLE; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + + if (ATK_IS_ACTION (obj)) + { + itf = ATSPI_DBUS_INTERFACE_ACTION; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (atk_object_get_role (obj) == ATK_ROLE_APPLICATION) + { + itf = ATSPI_DBUS_INTERFACE_APPLICATION; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_COMPONENT (obj)) + { + itf = ATSPI_DBUS_INTERFACE_COMPONENT; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_EDITABLE_TEXT (obj)) + { + itf = ATSPI_DBUS_INTERFACE_EDITABLE_TEXT; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_TEXT (obj)) + { + itf = ATSPI_DBUS_INTERFACE_TEXT; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_HYPERTEXT (obj)) + { + itf = ATSPI_DBUS_INTERFACE_HYPERTEXT; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_IMAGE (obj)) + { + itf = ATSPI_DBUS_INTERFACE_IMAGE; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_SELECTION (obj)) + { + itf = ATSPI_DBUS_INTERFACE_SELECTION; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_TABLE (obj)) + { + itf = ATSPI_DBUS_INTERFACE_TABLE; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_TABLE_CELL (obj)) + { + itf = ATSPI_DBUS_INTERFACE_TABLE_CELL; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_VALUE (obj)) + { + itf = ATSPI_DBUS_INTERFACE_VALUE; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + +#if 0 + if (ATK_IS_STREAMABLE_CONTENT (obj)) + { + itf = "org.a11y.atspi.StreamableContent"; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } +#endif + + if (ATK_IS_OBJECT (obj)) + { + itf = "org.a11y.atspi.Collection"; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_DOCUMENT (obj)) + { + itf = ATSPI_DBUS_INTERFACE_DOCUMENT; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } + + if (ATK_IS_HYPERLINK_IMPL (obj)) + { + itf = ATSPI_DBUS_INTERFACE_HYPERLINK; + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf); + } +} + +/*---------------------------------------------------------------------------*/ + +void +spi_object_append_attribute_set (DBusMessageIter * iter, AtkAttributeSet * attr) +{ + DBusMessageIter dictIter; + + dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, "{ss}", &dictIter); + while (attr) + { + DBusMessageIter dictEntryIter; + AtkAttribute *attribute = (AtkAttribute *) attr->data; + const char *key = attribute->name; + const char *value = attribute->value; + + if (key == NULL) + key = ""; + if (value == NULL) + value = ""; + + dbus_message_iter_open_container (&dictIter, DBUS_TYPE_DICT_ENTRY, NULL, + &dictEntryIter); + dbus_message_iter_append_basic (&dictEntryIter, DBUS_TYPE_STRING, + &key); + dbus_message_iter_append_basic (&dictEntryIter, DBUS_TYPE_STRING, + &value); + dbus_message_iter_close_container (&dictIter, &dictEntryIter); + attr = g_slist_next (attr); + } + dbus_message_iter_close_container (iter, &dictIter); +} + +/*---------------------------------------------------------------------------*/ + +static gboolean +init_role_lookup_table (AtspiRole * role_table) +{ + int i; + /* if it's not in the list below, dunno what it is */ + for (i = 0; i < ATK_ROLE_LAST_DEFINED; ++i) + { + role_table[i] = ATSPI_ROLE_UNKNOWN; + } + + role_table[ATK_ROLE_INVALID] = ATSPI_ROLE_INVALID; + role_table[ATK_ROLE_ACCEL_LABEL] = ATSPI_ROLE_ACCELERATOR_LABEL; + role_table[ATK_ROLE_ALERT] = ATSPI_ROLE_ALERT; + role_table[ATK_ROLE_ANIMATION] = ATSPI_ROLE_ANIMATION; + role_table[ATK_ROLE_ARROW] = ATSPI_ROLE_ARROW; + role_table[ATK_ROLE_CALENDAR] = ATSPI_ROLE_CALENDAR; + role_table[ATK_ROLE_CANVAS] = ATSPI_ROLE_CANVAS; + role_table[ATK_ROLE_CHECK_BOX] = ATSPI_ROLE_CHECK_BOX; + role_table[ATK_ROLE_CHECK_MENU_ITEM] = ATSPI_ROLE_CHECK_MENU_ITEM; + role_table[ATK_ROLE_COLOR_CHOOSER] = ATSPI_ROLE_COLOR_CHOOSER; + role_table[ATK_ROLE_COLUMN_HEADER] = ATSPI_ROLE_COLUMN_HEADER; + role_table[ATK_ROLE_COMBO_BOX] = ATSPI_ROLE_COMBO_BOX; + role_table[ATK_ROLE_DATE_EDITOR] = ATSPI_ROLE_DATE_EDITOR; + role_table[ATK_ROLE_DESKTOP_ICON] = ATSPI_ROLE_DESKTOP_ICON; + role_table[ATK_ROLE_DESKTOP_FRAME] = ATSPI_ROLE_DESKTOP_FRAME; + role_table[ATK_ROLE_DIAL] = ATSPI_ROLE_DIAL; + role_table[ATK_ROLE_DIALOG] = ATSPI_ROLE_DIALOG; + role_table[ATK_ROLE_DIRECTORY_PANE] = ATSPI_ROLE_DIRECTORY_PANE; + role_table[ATK_ROLE_DRAWING_AREA] = ATSPI_ROLE_DRAWING_AREA; + role_table[ATK_ROLE_FILE_CHOOSER] = ATSPI_ROLE_FILE_CHOOSER; + role_table[ATK_ROLE_FILLER] = ATSPI_ROLE_FILLER; + role_table[ATK_ROLE_FONT_CHOOSER] = ATSPI_ROLE_FONT_CHOOSER; + role_table[ATK_ROLE_FRAME] = ATSPI_ROLE_FRAME; + role_table[ATK_ROLE_GLASS_PANE] = ATSPI_ROLE_GLASS_PANE; + role_table[ATK_ROLE_HTML_CONTAINER] = ATSPI_ROLE_HTML_CONTAINER; + role_table[ATK_ROLE_ICON] = ATSPI_ROLE_ICON; + role_table[ATK_ROLE_IMAGE] = ATSPI_ROLE_IMAGE; + role_table[ATK_ROLE_INTERNAL_FRAME] = ATSPI_ROLE_INTERNAL_FRAME; + role_table[ATK_ROLE_LABEL] = ATSPI_ROLE_LABEL; + role_table[ATK_ROLE_LAYERED_PANE] = ATSPI_ROLE_LAYERED_PANE; + role_table[ATK_ROLE_LIST] = ATSPI_ROLE_LIST; + role_table[ATK_ROLE_LIST_ITEM] = ATSPI_ROLE_LIST_ITEM; + role_table[ATK_ROLE_MENU] = ATSPI_ROLE_MENU; + role_table[ATK_ROLE_MENU_BAR] = ATSPI_ROLE_MENU_BAR; + role_table[ATK_ROLE_MENU_ITEM] = ATSPI_ROLE_MENU_ITEM; + role_table[ATK_ROLE_OPTION_PANE] = ATSPI_ROLE_OPTION_PANE; + role_table[ATK_ROLE_PAGE_TAB] = ATSPI_ROLE_PAGE_TAB; + role_table[ATK_ROLE_PAGE_TAB_LIST] = ATSPI_ROLE_PAGE_TAB_LIST; + role_table[ATK_ROLE_PANEL] = ATSPI_ROLE_PANEL; + role_table[ATK_ROLE_PASSWORD_TEXT] = ATSPI_ROLE_PASSWORD_TEXT; + role_table[ATK_ROLE_POPUP_MENU] = ATSPI_ROLE_POPUP_MENU; + role_table[ATK_ROLE_PROGRESS_BAR] = ATSPI_ROLE_PROGRESS_BAR; + role_table[ATK_ROLE_PUSH_BUTTON] = ATSPI_ROLE_PUSH_BUTTON; + role_table[ATK_ROLE_RADIO_BUTTON] = ATSPI_ROLE_RADIO_BUTTON; + role_table[ATK_ROLE_RADIO_MENU_ITEM] = ATSPI_ROLE_RADIO_MENU_ITEM; + role_table[ATK_ROLE_ROOT_PANE] = ATSPI_ROLE_ROOT_PANE; + role_table[ATK_ROLE_ROW_HEADER] = ATSPI_ROLE_ROW_HEADER; + role_table[ATK_ROLE_SCROLL_BAR] = ATSPI_ROLE_SCROLL_BAR; + role_table[ATK_ROLE_SCROLL_PANE] = ATSPI_ROLE_SCROLL_PANE; + role_table[ATK_ROLE_SEPARATOR] = ATSPI_ROLE_SEPARATOR; + role_table[ATK_ROLE_SLIDER] = ATSPI_ROLE_SLIDER; + role_table[ATK_ROLE_SPIN_BUTTON] = ATSPI_ROLE_SPIN_BUTTON; + role_table[ATK_ROLE_SPLIT_PANE] = ATSPI_ROLE_SPLIT_PANE; + role_table[ATK_ROLE_STATUSBAR] = ATSPI_ROLE_STATUS_BAR; + role_table[ATK_ROLE_TABLE] = ATSPI_ROLE_TABLE; + role_table[ATK_ROLE_TABLE_CELL] = ATSPI_ROLE_TABLE_CELL; + role_table[ATK_ROLE_TABLE_COLUMN_HEADER] = + ATSPI_ROLE_TABLE_COLUMN_HEADER; + role_table[ATK_ROLE_TABLE_ROW_HEADER] = ATSPI_ROLE_TABLE_ROW_HEADER; + role_table[ATK_ROLE_TEAR_OFF_MENU_ITEM] = + ATSPI_ROLE_TEAROFF_MENU_ITEM; + role_table[ATK_ROLE_TERMINAL] = ATSPI_ROLE_TERMINAL; + role_table[ATK_ROLE_TEXT] = ATSPI_ROLE_TEXT; + role_table[ATK_ROLE_TOGGLE_BUTTON] = ATSPI_ROLE_TOGGLE_BUTTON; + role_table[ATK_ROLE_TOOL_BAR] = ATSPI_ROLE_TOOL_BAR; + role_table[ATK_ROLE_TOOL_TIP] = ATSPI_ROLE_TOOL_TIP; + role_table[ATK_ROLE_TREE] = ATSPI_ROLE_TREE; + role_table[ATK_ROLE_TREE_TABLE] = ATSPI_ROLE_TREE_TABLE; + role_table[ATK_ROLE_UNKNOWN] = ATSPI_ROLE_UNKNOWN; + role_table[ATK_ROLE_VIEWPORT] = ATSPI_ROLE_VIEWPORT; + role_table[ATK_ROLE_WINDOW] = ATSPI_ROLE_WINDOW; + role_table[ATK_ROLE_HEADER] = ATSPI_ROLE_HEADER; + role_table[ATK_ROLE_FOOTER] = ATSPI_ROLE_FOOTER; + role_table[ATK_ROLE_PARAGRAPH] = ATSPI_ROLE_PARAGRAPH; + role_table[ATK_ROLE_RULER] = ATSPI_ROLE_RULER; + role_table[ATK_ROLE_APPLICATION] = ATSPI_ROLE_APPLICATION; + role_table[ATK_ROLE_AUTOCOMPLETE] = ATSPI_ROLE_AUTOCOMPLETE; + role_table[ATK_ROLE_EDITBAR] = ATSPI_ROLE_EDITBAR; + role_table[ATK_ROLE_EMBEDDED] = ATSPI_ROLE_EMBEDDED; + role_table[ATK_ROLE_ENTRY] = ATSPI_ROLE_ENTRY; + role_table[ATK_ROLE_CHART] = ATSPI_ROLE_CHART; + role_table[ATK_ROLE_CAPTION] = ATSPI_ROLE_CAPTION; + role_table[ATK_ROLE_DOCUMENT_FRAME] = ATSPI_ROLE_DOCUMENT_FRAME; + role_table[ATK_ROLE_HEADING] = ATSPI_ROLE_HEADING; + role_table[ATK_ROLE_PAGE] = ATSPI_ROLE_PAGE; + role_table[ATK_ROLE_SECTION] = ATSPI_ROLE_SECTION; + role_table[ATK_ROLE_FORM] = ATSPI_ROLE_FORM; + role_table[ATK_ROLE_REDUNDANT_OBJECT] = ATSPI_ROLE_REDUNDANT_OBJECT; + role_table[ATK_ROLE_LINK] = ATSPI_ROLE_LINK; + role_table[ATK_ROLE_INPUT_METHOD_WINDOW] = + ATSPI_ROLE_INPUT_METHOD_WINDOW; + role_table[ATK_ROLE_TABLE_ROW] = ATSPI_ROLE_TABLE_ROW; + role_table[ATK_ROLE_TREE_ITEM] = ATSPI_ROLE_TREE_ITEM; + role_table[ATK_ROLE_DOCUMENT_SPREADSHEET] = + ATSPI_ROLE_DOCUMENT_SPREADSHEET; + role_table[ATK_ROLE_DOCUMENT_PRESENTATION] = + ATSPI_ROLE_DOCUMENT_PRESENTATION; + role_table[ATK_ROLE_DOCUMENT_TEXT] = ATSPI_ROLE_DOCUMENT_TEXT; + role_table[ATK_ROLE_DOCUMENT_WEB] = ATSPI_ROLE_DOCUMENT_WEB; + role_table[ATK_ROLE_DOCUMENT_EMAIL] = ATSPI_ROLE_DOCUMENT_EMAIL; + role_table[ATK_ROLE_COMMENT] = ATSPI_ROLE_COMMENT; + role_table[ATK_ROLE_LIST_BOX] = ATSPI_ROLE_LIST_BOX; + role_table[ATK_ROLE_GROUPING] = ATSPI_ROLE_GROUPING; + role_table[ATK_ROLE_IMAGE_MAP] = ATSPI_ROLE_IMAGE_MAP; + role_table[ATK_ROLE_NOTIFICATION] = ATSPI_ROLE_NOTIFICATION; + role_table[ATK_ROLE_INFO_BAR] = ATSPI_ROLE_INFO_BAR; + role_table[ATK_ROLE_LEVEL_BAR] = ATSPI_ROLE_LEVEL_BAR; + role_table[ATK_ROLE_TITLE_BAR] = ATSPI_ROLE_TITLE_BAR; + role_table[ATK_ROLE_BLOCK_QUOTE] = ATSPI_ROLE_BLOCK_QUOTE; + role_table[ATK_ROLE_AUDIO] = ATSPI_ROLE_AUDIO; + role_table[ATK_ROLE_VIDEO] = ATSPI_ROLE_VIDEO; + role_table[ATK_ROLE_DEFINITION] = ATSPI_ROLE_DEFINITION; + role_table[ATK_ROLE_ARTICLE] = ATSPI_ROLE_ARTICLE; + role_table[ATK_ROLE_LANDMARK] = ATSPI_ROLE_LANDMARK; + role_table[ATK_ROLE_LOG] = ATSPI_ROLE_LOG; + role_table[ATK_ROLE_MARQUEE] = ATSPI_ROLE_MARQUEE; + role_table[ATK_ROLE_MATH] = ATSPI_ROLE_MATH; + role_table[ATK_ROLE_RATING] = ATSPI_ROLE_RATING; + role_table[ATK_ROLE_TIMER] = ATSPI_ROLE_TIMER; + + return TRUE; +} + +AtspiRole +spi_accessible_role_from_atk_role (AtkRole role) +{ + static gboolean is_initialized = FALSE; + static AtspiRole spi_role_table[ATK_ROLE_LAST_DEFINED]; + AtspiRole spi_role; + + if (!is_initialized) + { + is_initialized = init_role_lookup_table (spi_role_table); + } + + if (role >= 0 && role < ATK_ROLE_LAST_DEFINED) + { + spi_role = spi_role_table[role]; + } + else + { + spi_role = ATSPI_ROLE_EXTENDED; + } + return spi_role; +} + +/*END------------------------------------------------------------------------*/ diff --git a/atk-adaptor/object.h b/atk-adaptor/object.h new file mode 100644 index 0000000..5f283bc --- /dev/null +++ b/atk-adaptor/object.h @@ -0,0 +1,63 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008, 2009, 2010 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ACCESSIBLE_OBJECT_H +#define ACCESSIBLE_OBJECT_H + +#include <atk/atk.h> +#include <dbus/dbus.h> + +void +spi_object_lease_if_needed (GObject *obj); + +void +spi_object_append_reference (DBusMessageIter * iter, AtkObject * obj); + +void +spi_hyperlink_append_reference (DBusMessageIter * iter, AtkObject * obj); + +void +spi_object_append_v_reference (DBusMessageIter * iter, AtkObject * obj); + +void +spi_object_append_desktop_reference (DBusMessageIter * iter); + +void +spi_object_append_null_reference (DBusMessageIter * iter); + +DBusMessage * +spi_object_return_reference (DBusMessage * msg, AtkObject * obj); + +DBusMessage * +spi_hyperlink_return_reference (DBusMessage * msg, AtkHyperlink * obj); + +void +spi_object_append_interfaces (DBusMessageIter * iter, AtkObject * obj); + +void +spi_object_append_attribute_set (DBusMessageIter * iter, AtkAttributeSet * attr); + +AtspiRole +spi_accessible_role_from_atk_role (AtkRole role); + +#endif /* ACCESSIBLE_OBJECT_H */ diff --git a/atk-adaptor/spi-dbus.c b/atk-adaptor/spi-dbus.c new file mode 100644 index 0000000..e757376 --- /dev/null +++ b/atk-adaptor/spi-dbus.c @@ -0,0 +1,289 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> +#include <glib-object.h> +#include <dbus/dbus.h> + +#include <atspi/atspi.h> + +DBusMessage * +spi_dbus_general_error (DBusMessage * message) +{ + return dbus_message_new_error (message, + "org.a11y.atspi.GeneralError", + "General error"); +} + +DBusMessage * +spi_dbus_return_rect (DBusMessage * message, gint ix, gint iy, gint iwidth, + gint iheight) +{ + DBusMessage *reply; + dbus_uint32_t x, y, width, height; + + x = ix; + y = iy; + width = iwidth; + height = iheight; + reply = dbus_message_new_method_return (message); + if (reply) + { + DBusMessageIter iter, sub; + dbus_message_iter_init_append (reply, &iter); + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_STRUCT, NULL, &sub)) + goto oom; + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &x); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &y); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &width); + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &height); + if (!dbus_message_iter_close_container (&iter, &sub)) + goto oom; + } + return reply; +oom: + /* todo: return an error */ + return reply; +} + +void spi_dbus_emit_valist(DBusConnection *bus, const char *path, const char *interface, const char *name, int first_arg_type, va_list args) +{ + DBusMessage *sig; + + sig = dbus_message_new_signal(path, interface, name); + if (first_arg_type != DBUS_TYPE_INVALID) + { + dbus_message_append_args_valist(sig, first_arg_type, args); + } + dbus_connection_send(bus, sig, NULL); + dbus_message_unref(sig); +} + +dbus_bool_t spi_dbus_message_iter_get_struct(DBusMessageIter *iter, ...) +{ + va_list args; + DBusMessageIter iter_struct; + int type; + void *ptr; + + dbus_message_iter_recurse(iter, &iter_struct); + va_start(args, iter); + for (;;) + { + type = va_arg(args, int); + if (type == DBUS_TYPE_INVALID) break; + if (type != dbus_message_iter_get_arg_type(&iter_struct)) + { + va_end(args); + return FALSE; + } + ptr = va_arg(args, void *); + dbus_message_iter_get_basic(&iter_struct, ptr); + dbus_message_iter_next(&iter_struct); + } + dbus_message_iter_next(iter); + va_end(args); + return TRUE; +} + +dbus_bool_t spi_dbus_message_iter_append_struct(DBusMessageIter *iter, ...) +{ + va_list args; + DBusMessageIter iter_struct; + int type; + void *ptr; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &iter_struct)) return FALSE; + va_start(args, iter); + for (;;) + { + type = va_arg(args, int); + if (type == DBUS_TYPE_INVALID) break; + ptr = va_arg(args, void *); + dbus_message_iter_append_basic(&iter_struct, type, ptr); + } + va_end(args); + if (!dbus_message_iter_close_container(iter, &iter_struct)) return FALSE; + return TRUE; +} + +dbus_bool_t spi_dbus_marshal_deviceEvent(DBusMessage *message, const AtspiDeviceEvent *e) +{ + DBusMessageIter iter; + + if (!message) return FALSE; + dbus_message_iter_init_append(message, &iter); + return spi_dbus_message_iter_append_struct(&iter, DBUS_TYPE_UINT32, &e->type, DBUS_TYPE_INT32, &e->id, DBUS_TYPE_INT16, &e->hw_code, DBUS_TYPE_INT16, &e->modifiers, DBUS_TYPE_INT32, &e->timestamp, DBUS_TYPE_STRING, &e->event_string, DBUS_TYPE_BOOLEAN, &e->is_text, DBUS_TYPE_INVALID); +} + +dbus_bool_t spi_dbus_demarshal_deviceEvent(DBusMessage *message, AtspiDeviceEvent *e) +{ + DBusMessageIter iter; + + dbus_message_iter_init(message, &iter); + return spi_dbus_message_iter_get_struct(&iter, DBUS_TYPE_UINT32, &e->type, DBUS_TYPE_INT32, &e->id, DBUS_TYPE_INT16, &e->hw_code, DBUS_TYPE_INT16, &e->modifiers, DBUS_TYPE_INT32, &e->timestamp, DBUS_TYPE_STRING, &e->event_string, DBUS_TYPE_BOOLEAN, &e->is_text, DBUS_TYPE_INVALID); +} + +/* + * This is a rather annoying function needed to replace + * NULL values of strings with the empty string. Null string + * values can be created by the atk_object_get_name or text selection + */ +static const void * +provide_defaults(const gint type, + const void *val) +{ + switch (type) + { + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + if (!val) + return ""; + else + return val; + default: + return val; + } +} + +/* + * Appends all the standard parameters to an AT-SPI event. + */ +void +spi_dbus_signal_new (const char *path, + const char *klass, + const char *major, + const char *minor, + dbus_int32_t detail1, + dbus_int32_t detail2) +{ + DBusMessage *sig; + DBusMessageIter iter; + gchar *cname, *t; + + if (!klass) klass = ""; + if (!major) major = ""; + if (!minor) minor = ""; + + /* + * This is very annoying, but as '-' isn't a legal signal + * name in D-Bus (Why not??!?) The names need converting + * on this side, and again on the client side. + */ + cname = g_strdup(major); + while ((t = strchr(cname, '-')) != NULL) *t = '_'; + + sig = dbus_message_new_signal(path, klass, cname); + g_free(cname); + + dbus_message_iter_init_append(sig, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2); +} + +void +spi_dbus_emit_signal(DBusConnection *bus, const char *path, + const char *klass, + const char *major, + const char *minor, + dbus_int32_t detail1, + dbus_int32_t detail2, + const char *type, + const void *val) +{ + gchar *cname, *t; + DBusMessage *sig; + DBusMessageIter iter, sub; + if (!klass) klass = ""; + if (!major) major = ""; + if (!minor) minor = ""; + if (!type) type = "u"; + + /* + * This is very annoying, but as '-' isn't a legal signal + * name in D-Bus (Why not??!?) The names need converting + * on this side, and again on the client side. + */ + cname = g_strdup(major); + while ((t = strchr(cname, '-')) != NULL) *t = '_'; + + sig = dbus_message_new_signal(path, klass, cname); + g_free(cname); + + dbus_message_iter_init_append(sig, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type, &sub); + /* + * I need to convert the string signature to an integer type signature. + * DBUS_TYPE_INT32 is defined as 'i' whereas the string is "i". + * I should just be able to cast the first character of the string to an + * integer. + */ + val = provide_defaults((int) *type, val); + dbus_message_iter_append_basic(&sub, (int) *type, &val); + dbus_message_iter_close_container(&iter, &sub); + + dbus_connection_send(bus, sig, NULL); + dbus_message_unref(sig); +} + + +/* +dbus_bool_t spi_dbus_get_simple_property (DBusConnection *bus, const char *dest, const char *path, const char *interface, const char *prop, int *type, void *ptr, DBusError *error) +{ + DBusMessage *message, *reply; + DBusMessageIter iter, iter_variant; + int typ; + + dbus_error_init (error); + message = dbus_message_new_method_call (dest, path, "org.freedesktop.DBus.Properties", "get"); + if (!message) return FALSE; + if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID)) + { + return FALSE; + } + reply = dbus_connection_send_with_reply_and_block (bus, message, 1000, error); + dbus_message_unref (message); + if (!reply) return FALSE; + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_variant); + typ = dbus_message_iter_get_arg_type (&iter_variant); + if (type) *type = typ; + if (typ == DBUS_TYPE_INVALID || typ == DBUS_TYPE_STRUCT || typ == DBUS_TYPE_ARRAY) + { + return FALSE; + } + dbus_message_iter_get_basic (&iter_variant, ptr); + dbus_message_unref (reply); + return TRUE; +} +*/ diff --git a/atk-adaptor/spi-dbus.h b/atk-adaptor/spi-dbus.h new file mode 100644 index 0000000..61a43be --- /dev/null +++ b/atk-adaptor/spi-dbus.h @@ -0,0 +1,56 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SPI_DBUS_H_ +#define SPI_DBUS_H_ + +#include <glib.h> +#include <atspi/atspi.h> + +#define DBG(a,b) if(_dbg>=(a))b + +extern int _dbg; + +#define SPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry" +#define SPI_DBUS_PATH_REGISTRY "/org/a11y/atspi/registry" +#define SPI_DBUS_INTERFACE_REGISTRY "org.a11y.atspi.Registry" + +#define SPI_DBUS_PATH_NULL "/org/a11y/atspi/null" +#define SPI_DBUS_PATH_ROOT "/org/a11y/atspi/accessible/root" + +DBusMessage *spi_dbus_general_error(DBusMessage *message); +DBusMessage *spi_dbus_return_rect(DBusMessage *message, gint ix, gint iy, gint iwidth, gint iheight); + +void spi_dbus_emit_valist(DBusConnection *bus, const char *path, const char *interface, const char *name, int first_arg_type, va_list args); +dbus_bool_t spi_dbus_message_iter_get_struct(DBusMessageIter *iter, ...); +dbus_bool_t spi_dbus_message_iter_append_struct(DBusMessageIter *iter, ...); +dbus_bool_t spi_dbus_marshal_deviceEvent(DBusMessage *message, const AtspiDeviceEvent *e); +dbus_bool_t spi_dbus_demarshal_deviceEvent(DBusMessage *message, AtspiDeviceEvent *e); +dbus_bool_t spi_dbus_get_simple_property (DBusConnection *bus, const char *dest, const char *path, const char *interface, const char *prop, int *type, void *ptr, DBusError *error); +void spi_dbus_emit_signal(DBusConnection *bus, const char *path, const char *klass, const char *major, const char *minor, dbus_int32_t detail1, dbus_int32_t detail2, const char *type, const void *val); +/* +void spi_dbus_add_disconnect_match (DBusConnection *bus, const char *name); +void spi_dbus_remove_disconnect_match (DBusConnection *bus, const char *name); +*/ + +#endif /* SPI_DBUS_H_ */ diff --git a/atk-bridge-2.0.pc.in b/atk-bridge-2.0.pc.in new file mode 100644 index 0000000..6af5861 --- /dev/null +++ b/atk-bridge-2.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: atk-bridge-2.0 +Description: ATK/D-Bus Bridge +Version: @VERSION@ +Requires.Private: gobject-2.0 atspi-2 +Libs: -L${libdir} -latk-bridge-2.0 +Cflags: -I${includedir}/at-spi2-atk/2.0 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..78dabeb --- /dev/null +++ b/autogen.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd "$srcdir" + +# gnome-autogen.sh runs configure, so do likewise. +autoreconf -vif + +cd "$olddir" + +test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d7f5c73 --- /dev/null +++ b/configure.ac @@ -0,0 +1,103 @@ +AC_INIT([at-spi2-atk], [2.12.0], [accessibility-atspi@lists.linux-foundation.org]) +AC_CONFIG_AUX_DIR(config) + +AT_SPI_ATK_MAJOR_VERSION=0 +AT_SPI_ATK_MINOR_VERSION=3 +AT_SPI_ATK_INTERFACE_AGE=0 +AT_SPI_ATK_BINARY_AGE=0 +AT_SPI_ATK_VERSION="$AT_SPI_MAJOR_VERSION.$AT_SPI_MINOR_VERSION" +AC_SUBST(AT_SPI_ATK_VERSION) + +# libtool versioning +LT_RELEASE=$AT_SPI_MAJOR_VERSION.$AT_SPI_MINOR_VERSION +LT_CURRENT=0 +LT_REVISION=0 +LT_AGE=0 +LT_VERSION_INFO='-version-info ${LT_CURRENT}:${LT_REVISION}:${LT_AGE}' +AC_SUBST(LT_VERSION_INFO) +AC_SUBST(LT_RELEASE) +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) + +AM_INIT_AUTOMAKE([-Wall foreign no-dist-gzip dist-xz]) + +# Enable silent build when available (Automake 1.11) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) + +AC_PROG_CC +AM_DISABLE_STATIC +AM_PROG_LIBTOOL +PKG_PROG_PKG_CONFIG + +GETTEXT_PACKAGE="${PACKAGE}" +AC_SUBST(GETTEXT_PACKAGE) + +AC_CONFIG_HEADERS([config.h]) + +PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.5]) +AC_SUBST(DBUS_LIBS) +AC_SUBST(DBUS_CFLAGS) + +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.32.0]) +AC_SUBST(GLIB_LIBS) +AC_SUBST(GLIB_CFLAGS) + +PKG_CHECK_MODULES(GOBJ, [gobject-2.0 >= 2.0.0]) +AC_SUBST(GOBJ_LIBS) +AC_SUBST(GOBJ_CFLAGS) + +PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.0.0]) +AC_SUBST(GMODULE_LIBS) +AC_SUBST(GMODULE_CFLAGS) + +PKG_CHECK_MODULES(ATK, [atk >= 2.11.90]) +AC_SUBST(ATK_LIBS) +AC_SUBST(ATK_CFLAGS) + +PKG_CHECK_MODULES(ATSPI, [atspi-2 >= 2.11.2]) +AC_SUBST(ATSPI_LIBS) +AC_SUBST(ATSPI_CFLAGS) + +GLIB_GSETTINGS + +AC_ARG_ENABLE(p2p, [ --enable-p2p Allow peer-to-peer DBus connections [default=yes]], enable_p2p="$enableval", enable_p2p=yes) + +#libtool option to strip symbols starting with cspi +LIBTOOL_EXPORT_OPTIONS='-export-symbols-regex "^[[^cspi]].*"' +AC_SUBST(LIBTOOL_EXPORT_OPTIONS) + +# Extra libraries for sockets added by Willie Walker +# based upon how SunStudio libraries work. +# +if test "$GCC" = yes; then + EXTRA_SOCKET_LIBS="" +else + EXTRA_SOCKET_LIBS="-lsocket -lnsl" +fi +AC_SUBST(EXTRA_SOCKET_LIBS) + +dnl find sizes & alignments +orig_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $DBUS_CFLAGS" +CPPFLAGS=$orig_CPPFLAGS + +if test "x$GCC" = xyes; then + CFLAGS="$CFLAGS -Werror-implicit-function-declaration" +fi + +if test "x$enable_p2p" = "xno"; then + P2P_CFLAGS=-DDISABLE_P2P +fi + +AC_SUBST(P2P_CFLAGS) + +AC_CONFIG_FILES([Makefile + atk-bridge-2.0.pc + droute/Makefile + atk-adaptor/Makefile + atk-adaptor/adaptors/Makefile + atk-adaptor/gtk-2.0/Makefile + ]) + +AC_OUTPUT diff --git a/droute/Makefile.am b/droute/Makefile.am new file mode 100644 index 0000000..f214d19 --- /dev/null +++ b/droute/Makefile.am @@ -0,0 +1,30 @@ +noinst_LTLIBRARIES = libdroute.la + +libdroute_la_CFLAGS = $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + -I$(top_builddir)\ + -I$(top_srcdir) + +libdroute_la_SOURCES =\ + droute.c\ + droute.h\ + droute-variant.c\ + droute-variant.h\ + droute-pairhash.c\ + droute-pairhash.h +libdroute_la_LIBADD = $(DBUS_LIBS) + +TESTS = droute-test + +check_PROGRAMS = droute-test +droute_test_SOURCES = droute-test.c +droute_test_CFLAGS = $(DBUS_CFLAGS) \ + -I$(top_builddir)\ + $(GLIB_CFLAGS) \ + $(ATSPI_CFLAGS) \ + -I$(top_srcdir) + +droute_test_LDFLAGS = libdroute.la\ + $(DBUS_LIBS) \ + $(GLIB_LIBS) \ + $(ATSPI_LIBS) diff --git a/droute/droute-pairhash.c b/droute/droute-pairhash.c new file mode 100644 index 0000000..c2f2c29 --- /dev/null +++ b/droute/droute-pairhash.c @@ -0,0 +1,87 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "droute-pairhash.h" + +/*---------------------------------------------------------------------------*/ + +static guint +str_hash (guint32 h, const char *p) +{ + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + return h; +} + +/*---------------------------------------------------------------------------*/ + +StrPair * +str_pair_new (const gchar *one, const gchar *two) +{ + StrPair *pair; + + pair = g_new (StrPair, 1); + pair->one = one; + pair->two = two; + return pair; +} + +guint +str_pair_hash (gconstpointer key) +{ + StrPair *pair = (StrPair *) key; + guint hash = 0; + + /*g_return_val_if_fail (pair != NULL, 0); + g_return_val_if_fail (pair->one != NULL, 0); + g_return_val_if_fail (pair->two != NULL, 0); + */ + + if (*(pair->two) != '\0') + { + hash = *(pair->two); + hash = str_hash (hash, pair->two); + hash = str_hash (hash, pair->one); + } + + return hash; +} + +gboolean +str_pair_equal (gconstpointer a, gconstpointer b) +{ + StrPair *ap = (StrPair *) a; + StrPair *bp = (StrPair *) b; + + if (g_str_equal (ap->one, bp->one) && + g_str_equal (ap->two, bp->two)) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/*END------------------------------------------------------------------------*/ diff --git a/droute/droute-pairhash.h b/droute/droute-pairhash.h new file mode 100644 index 0000000..1491c2d --- /dev/null +++ b/droute/droute-pairhash.h @@ -0,0 +1,41 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _DROUTE_PAIRHASH_H +#define _DROUTE_PAIRHASH_H + +#include <glib.h> + +typedef struct _StrPair StrPair; +struct _StrPair +{ + const gchar *one; + const gchar *two; +}; + +StrPair *str_pair_new (const gchar *one, + const gchar *two); + +guint str_pair_hash (gconstpointer key); +gboolean str_pair_equal (gconstpointer a, + gconstpointer b); + +#endif /* _DROUTE_PAIRHASH_H */ diff --git a/droute/droute-test.c b/droute/droute-test.c new file mode 100644 index 0000000..1865622 --- /dev/null +++ b/droute/droute-test.c @@ -0,0 +1,308 @@ +#include <stdio.h> +#include <stdlib.h> +#include <glib.h> +#include <string.h> +#include <droute/droute.h> + +#include "atspi/atspi.h" + +#define TEST_OBJECT_PATH "/test/object" +#define TEST_INTERFACE_ONE "test.interface.One" +#define TEST_INTERFACE_TWO "test.interface.Two" + +#define OBJECT_ONE "ObjectOne"; +#define OBJECT_TWO "ObjectTwo"; + +#define STRING_ONE "StringOne" +#define STRING_TWO "StringTwo" + +#define INT_ONE 0 +#define INT_TWO 456 + +#define NONE_REPLY_STRING "NoneMethod" + +const gchar *test_interface_One = \ +"<interface name=\"test.interface.One\">" +" <method name=\"null\"/>" +" <method name=\"getInt\">" +" <arg direction=\"out\" type=\"o\"/>" +" </method>" +" <method name=\"setInt\">" +" <arg direction=\"in\" type=\"o\"/>" +" </method>" +" <method name=\"getString\">" +" <arg direction=\"out\" type=\"s\"/>" +" </method>" +" <method name=\"setString\">" +" <arg direction=\"in\" type=\"s\"/>" +" </method>" +"</interface>"; + +const gchar *test_interface_Two = \ +"<interface name=\"test.interface.One\">" +" <method name=\"null\"/>" +" <method name=\"getInt\">" +" <arg direction=\"out\" type=\"o\"/>" +" </method>" +" <method name=\"setInt\">" +" <arg direction=\"in\" type=\"o\"/>" +" </method>" +" <method name=\"getString\">" +" <arg direction=\"out\" type=\"s\"/>" +" </method>" +" <method name=\"setString\">" +" <arg direction=\"in\" type=\"s\"/>" +" </method>" +"</interface>"; + +typedef struct _AnObject +{ + gchar *astring; + guint *anint; +} AnObject; + +static DBusConnection *bus; +static GMainLoop *main_loop; +static gboolean success = TRUE; + +static DBusMessage * +impl_null (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return (message); + return reply; +} + +static DBusMessage * +impl_getInt (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + AnObject *object = (AnObject *) user_data; + DBusMessage *reply; + DBusError error; + + dbus_error_init (&error); + + reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_INT32, &(object->anint), DBUS_TYPE_INVALID); + return reply; +} + +static DBusMessage * +impl_setInt (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + AnObject *object = (AnObject *) user_data; + DBusMessage *reply; + DBusError error; + + dbus_error_init (&error); + + dbus_message_get_args (message, &error, DBUS_TYPE_INT32, &(object->anint), DBUS_TYPE_INVALID); + + reply = dbus_message_new_method_return (message); + return reply; +} + +static DBusMessage * +impl_getString (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + AnObject *object = (AnObject *) user_data; + DBusMessage *reply; + DBusError error; + + dbus_error_init (&error); + + reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_STRING, &(object->astring), DBUS_TYPE_INVALID); + return reply; +} + +static DBusMessage * +impl_setString (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + AnObject *object = (AnObject *) user_data; + DBusMessage *reply; + DBusError error; + + dbus_error_init (&error); + + g_free (object->astring); + dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &(object->astring), DBUS_TYPE_INVALID); + + reply = dbus_message_new_method_return (message); + return reply; +} + +static DBusMessage * +impl_getInterfaceOne (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + DBusMessage *reply; + DBusError error; + gchar *itf = TEST_INTERFACE_ONE; + + dbus_error_init (&error); + + reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_STRING, &itf, DBUS_TYPE_INVALID); + return reply; +} + +static DBusMessage * +impl_getInterfaceTwo (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + DBusMessage *reply; + DBusError error; + gchar *itf = TEST_INTERFACE_TWO; + + dbus_error_init (&error); + + reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_STRING, &itf, DBUS_TYPE_INVALID); + return reply; +} + +static DRouteMethod test_methods_one[] = { + {impl_null, "null"}, + {impl_getInt, "getInt"}, + {impl_setInt, "setInt"}, + {impl_getString, "getString"}, + {impl_setString, "setString"}, + {impl_getInterfaceOne, "getInterfaceOne"}, + {NULL, NULL} +}; + +static DRouteMethod test_methods_two[] = { + {impl_null, "null"}, + {impl_getInt, "getInt"}, + {impl_setInt, "setInt"}, + {impl_getString, "getString"}, + {impl_setString, "setString"}, + {impl_getInterfaceTwo, "getInterfaceTwo"}, + {NULL, NULL} +}; + +static DRouteProperty test_properties[] = { + {NULL, NULL, NULL} +}; + +static void +set_reply (DBusPendingCall *pending, void *user_data) +{ + void **replyptr = (void **)user_data; + + *replyptr = dbus_pending_call_steal_reply (pending); +} + +static DBusMessage * +send_and_allow_reentry (DBusConnection *bus, DBusMessage *message, DBusError *error) +{ + DBusPendingCall *pending; + DBusMessage *reply = NULL; + + if (!dbus_connection_send_with_reply (bus, message, &pending, -1)) + { + return NULL; + } + dbus_pending_call_set_notify (pending, set_reply, (void *)&reply, NULL); + while (!reply) + { + if (!dbus_connection_read_write_dispatch (bus, -1)) + return NULL; + } + return reply; +} + +gboolean +do_tests_func (gpointer data) +{ + DBusError error; + const gchar *bus_name; + DBusMessage *message, *reply; + + gchar *expected_string; + gchar *result_string; + + dbus_error_init (&error); + bus_name = dbus_bus_get_unique_name (bus); + + /* --------------------------------------------------------*/ + + message = dbus_message_new_method_call (bus_name, + TEST_OBJECT_PATH, + TEST_INTERFACE_ONE, + "null"); + reply = send_and_allow_reentry (bus, message, NULL); + dbus_message_unref (message); + if (reply) + dbus_message_unref (reply); + + /* --------------------------------------------------------*/ + + expected_string = TEST_INTERFACE_ONE; + result_string = NULL; + message = dbus_message_new_method_call (bus_name, + TEST_OBJECT_PATH, + TEST_INTERFACE_ONE, + "getInterfaceOne"); + reply = send_and_allow_reentry (bus, message, NULL); + dbus_message_unref (message); + dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &result_string, + DBUS_TYPE_INVALID); + dbus_message_unref (reply); + if (g_strcmp0(expected_string, result_string)) + { + g_print ("Failed: reply to getInterfaceOne was %s; expected %s\n", + result_string, expected_string); + exit (1); + } + + /* --------------------------------------------------------*/ + +out: + g_main_loop_quit (main_loop); + return FALSE; +} + + +int main (int argc, char **argv) +{ + DRouteContext *cnx; + DRoutePath *path; + AnObject *object; + DBusError error; + + /* Setup some server object */ + + object = g_new0(AnObject, 1); + object->astring = g_strdup (STRING_ONE); + object->anint = INT_ONE; + + dbus_error_init (&error); + main_loop = g_main_loop_new(NULL, FALSE); + bus = dbus_bus_get (DBUS_BUS_SESSION, &error); + atspi_dbus_connection_setup_with_g_main(bus, g_main_context_default()); + + cnx = droute_new (); + path = droute_add_one (cnx, TEST_OBJECT_PATH, object); + + droute_path_add_interface (path, + TEST_INTERFACE_ONE, + test_interface_One, + test_methods_one, + test_properties); + + droute_path_add_interface (path, + TEST_INTERFACE_TWO, + test_interface_Two, + test_methods_two, + test_properties); + + droute_path_register (path, bus); + + g_idle_add (do_tests_func, NULL); + g_main_loop_run(main_loop); + if (success) + return 0; + else + return 1; +} diff --git a/droute/droute-variant.c b/droute/droute-variant.c new file mode 100644 index 0000000..bd5ef36 --- /dev/null +++ b/droute/droute-variant.c @@ -0,0 +1,125 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include "glib.h" + +#include "droute-variant.h" + +/*---------------------------------------------------------------------------*/ + +dbus_bool_t +droute_return_v_int32 (DBusMessageIter *iter, dbus_int32_t val) +{ + DBusMessageIter sub; + + if (!dbus_message_iter_open_container + (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &sub)) + { + return FALSE; + } + dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &val); + dbus_message_iter_close_container (iter, &sub); + return TRUE; +} + +dbus_bool_t +droute_return_v_double (DBusMessageIter *iter, double val) +{ + DBusMessageIter sub; + + if (!dbus_message_iter_open_container + (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_DOUBLE_AS_STRING, &sub)) + { + return FALSE; + } + dbus_message_iter_append_basic (&sub, DBUS_TYPE_DOUBLE, &val); + dbus_message_iter_close_container (iter, &sub); + return TRUE; +} + +dbus_bool_t +droute_return_v_string (DBusMessageIter *iter, const char *val) +{ + DBusMessageIter sub; + + if (!val) + val = ""; + if (!g_utf8_validate (val, -1, NULL)) + { + g_warning ("droute: Received bad UTF-8 string"); + val = ""; + } + + if (!dbus_message_iter_open_container + (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &sub)) + { + return FALSE; + } + dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &val); + dbus_message_iter_close_container (iter, &sub); + return TRUE; +} + +dbus_bool_t +droute_return_v_object (DBusMessageIter *iter, const char *path) +{ + DBusMessageIter sub; + + if (!dbus_message_iter_open_container + (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_OBJECT_PATH_AS_STRING, &sub)) + { + return FALSE; + } + dbus_message_iter_append_basic (&sub, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_close_container (iter, &sub); + return TRUE; +} + +/*---------------------------------------------------------------------------*/ + +dbus_int32_t +droute_get_v_int32 (DBusMessageIter *iter) +{ + DBusMessageIter sub; + dbus_int32_t rv; + + // TODO- ensure we have the correct type + dbus_message_iter_recurse (iter, &sub); + dbus_message_iter_get_basic (&sub, &rv); + return rv; +} + +const char * +droute_get_v_string (DBusMessageIter *iter) +{ + DBusMessageIter sub; + char *rv; + + // TODO- ensure we have the correct type + dbus_message_iter_recurse (iter, &sub); + dbus_message_iter_get_basic (&sub, &rv); + return rv; +} + +/*END------------------------------------------------------------------------*/ diff --git a/droute/droute-variant.h b/droute/droute-variant.h new file mode 100644 index 0000000..47feb96 --- /dev/null +++ b/droute/droute-variant.h @@ -0,0 +1,35 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _DROUTE_VARIANT_H +#define _DROUTE_VARIANT_H + +#include <dbus/dbus.h> + +dbus_bool_t droute_return_v_int32 (DBusMessageIter *iter, dbus_int32_t val); +dbus_bool_t droute_return_v_double (DBusMessageIter *iter, double val); +dbus_bool_t droute_return_v_string (DBusMessageIter *iter, const char *val); +dbus_bool_t droute_return_v_object (DBusMessageIter *iter, const char *path); + +dbus_int32_t droute_get_v_int32 (DBusMessageIter *iter); +const char *droute_get_v_string (DBusMessageIter *iter); + +#endif /* _DROUTE_VARIANT_H */ diff --git a/droute/droute.c b/droute/droute.c new file mode 100644 index 0000000..838aacd --- /dev/null +++ b/droute/droute.c @@ -0,0 +1,742 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "droute.h" +#include "droute-pairhash.h" + +#define CHUNKS_DEFAULT (512) + +#define oom() g_error ("D-Bus out of memory, this message will fail anyway") + +#if defined DROUTE_DEBUG + #define _DROUTE_DEBUG(format, args...) g_print (format , ## args) +#else + #define _DROUTE_DEBUG(format, args...) +#endif + +struct _DRouteContext +{ + GPtrArray *registered_paths; + + gchar *introspect_string; +}; + +struct _DRoutePath +{ + DRouteContext *cnx; + gchar *path; + gboolean prefix; + GStringChunk *chunks; + GPtrArray *interfaces; + GPtrArray *introspection; + GHashTable *methods; + GHashTable *properties; + + DRouteIntrospectChildrenFunction introspect_children_cb; + void *introspect_children_data; + void *user_data; + DRouteGetDatumFunction get_datum; +}; + +/*---------------------------------------------------------------------------*/ + +typedef struct PropertyPair +{ + DRoutePropertyFunction get; + DRoutePropertyFunction set; +} PropertyPair; + +/*---------------------------------------------------------------------------*/ + +static DBusHandlerResult +handle_message (DBusConnection *bus, DBusMessage *message, void *user_data); + +static DBusMessage * +droute_object_does_not_exist_error (DBusMessage *message); + +/*---------------------------------------------------------------------------*/ + +static DRoutePath * +path_new (DRouteContext *cnx, + const char *path, + gboolean prefix, + void *user_data, + DRouteIntrospectChildrenFunction introspect_children_cb, + void *introspect_children_data, + DRouteGetDatumFunction get_datum) +{ + DRoutePath *new_path; + + new_path = g_new0 (DRoutePath, 1); + new_path->cnx = cnx; + new_path->path = g_strdup (path); + new_path->prefix = prefix; + new_path->chunks = g_string_chunk_new (CHUNKS_DEFAULT); + new_path->interfaces = g_ptr_array_new (); + new_path->introspection = g_ptr_array_new (); + + new_path->methods = g_hash_table_new_full ((GHashFunc)str_pair_hash, + str_pair_equal, + g_free, + NULL); + + new_path->properties = g_hash_table_new_full ((GHashFunc)str_pair_hash, + str_pair_equal, + g_free, + g_free); + + new_path->introspect_children_cb = introspect_children_cb; + new_path->introspect_children_data = introspect_children_data; + new_path->user_data = user_data; + new_path->get_datum = get_datum; + + return new_path; +} + +static void +path_free (DRoutePath *path, gpointer user_data) +{ + g_free (path->path); + g_string_chunk_free (path->chunks); + g_ptr_array_free (path->interfaces, TRUE); + g_free(g_ptr_array_free (path->introspection, FALSE)); + g_hash_table_destroy (path->methods); + g_hash_table_destroy (path->properties); + g_free (path); +} + +static void * +path_get_datum (DRoutePath *path, const gchar *pathstr) +{ + if (path->get_datum != NULL) + return (path->get_datum) (pathstr, path->user_data); + else + return path->user_data; +} + +/*---------------------------------------------------------------------------*/ + +DRouteContext * +droute_new () +{ + DRouteContext *cnx; + + cnx = g_new0 (DRouteContext, 1); + cnx->registered_paths = g_ptr_array_new (); + + return cnx; +} + +void +droute_free (DRouteContext *cnx) +{ + g_ptr_array_foreach (cnx->registered_paths, (GFunc) path_free, NULL); + g_ptr_array_free (cnx->registered_paths, TRUE); + g_free (cnx); +} + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ + +static DBusObjectPathVTable droute_vtable = +{ + NULL, + &handle_message, + NULL, NULL, NULL, NULL +}; + +DRoutePath * +droute_add_one (DRouteContext *cnx, + const char *path, + const void *data) +{ + DRoutePath *new_path; + + new_path = path_new (cnx, path, FALSE, (void *)data, NULL, NULL, NULL); + + g_ptr_array_add (cnx->registered_paths, new_path); + return new_path; +} + +DRoutePath * +droute_add_many (DRouteContext *cnx, + const char *path, + const void *data, + DRouteIntrospectChildrenFunction introspect_children_cb, + void *introspect_children_data, + const DRouteGetDatumFunction get_datum) +{ + DRoutePath *new_path; + + new_path = path_new (cnx, path, TRUE, (void *) data, + introspect_children_cb, introspect_children_data, + get_datum); + + g_ptr_array_add (cnx->registered_paths, new_path); + return new_path; +} + +/*---------------------------------------------------------------------------*/ + +void +droute_path_add_interface(DRoutePath *path, + const char *name, + const char *introspect, + const DRouteMethod *methods, + const DRouteProperty *properties) +{ + gchar *itf; + + g_return_if_fail (name != NULL); + + itf = g_string_chunk_insert (path->chunks, name); + g_ptr_array_add (path->interfaces, itf); + g_ptr_array_add (path->introspection, (gpointer) introspect); + + for (; methods != NULL && methods->name != NULL; methods++) + { + gchar *meth; + + meth = g_string_chunk_insert (path->chunks, methods->name); + g_hash_table_insert (path->methods, str_pair_new (itf, meth), methods->func); + } + + for (; properties != NULL && properties->name != NULL; properties++) + { + gchar *prop; + PropertyPair *pair; + + prop = g_string_chunk_insert (path->chunks, properties->name); + pair = g_new (PropertyPair, 1); + pair->get = properties->get; + pair->set = properties->set; + g_hash_table_insert (path->properties, str_pair_new (itf, prop), pair); + } +} + +/*---------------------------------------------------------------------------*/ + +/* The data structures don't support an efficient implementation of GetAll + * and I don't really care. + */ +static DBusMessage * +impl_prop_GetAll (DBusMessage *message, + DRoutePath *path, + const char *pathstr) +{ + DBusMessageIter iter, iter_dict, iter_dict_entry; + DBusMessage *reply; + DBusError error; + GHashTableIter prop_iter; + + StrPair *key; + PropertyPair *value; + gchar *iface; + + void *datum = path_get_datum (path, pathstr); + if (!datum) + return droute_object_does_not_exist_error (message); + + dbus_error_init (&error); + if (!dbus_message_get_args + (message, &error, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID)) + { + DBusMessage *ret; + ret = dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message); + dbus_error_free (&error); + return ret; + } + + reply = dbus_message_new_method_return (message); + if (!reply) + oom (); + + dbus_message_iter_init_append (reply, &iter); + if (!dbus_message_iter_open_container + (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict)) + oom (); + + g_hash_table_iter_init (&prop_iter, path->properties); + while (g_hash_table_iter_next (&prop_iter, (gpointer*)&key, (gpointer*)&value)) + { + if (!g_strcmp0 (key->one, iface)) + { + if (!value->get) + continue; + if (!dbus_message_iter_open_container + (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry)) + oom (); + dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, + &key->two); + (value->get) (&iter_dict_entry, datum); + if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry)) + oom (); + } + } + + if (!dbus_message_iter_close_container (&iter, &iter_dict)) + oom (); + return reply; +} + +static DBusMessage * +impl_prop_GetSet (DBusMessage *message, + DRoutePath *path, + const char *pathstr, + gboolean get) +{ + DBusMessage *reply = NULL; + DBusError error; + + StrPair pair; + PropertyPair *prop_funcs = NULL; + + void *datum; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, + &(pair.one), + DBUS_TYPE_STRING, + &(pair.two), + DBUS_TYPE_INVALID)) + { + DBusMessage *ret; + ret = dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message); + dbus_error_free (&error); + } + + _DROUTE_DEBUG ("DRoute (handle prop): %s|%s on %s\n", pair.one, pair.two, pathstr); + + prop_funcs = (PropertyPair *) g_hash_table_lookup (path->properties, &pair); + if (!prop_funcs) + { + DBusMessage *ret; +#ifdef DBUS_ERROR_UNKNOWN_PROPERTY + ret = dbus_message_new_error (message, DBUS_ERROR_UNKNOWN_PROPERTY, "Property unavailable"); +#else + ret = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable"); +#endif + dbus_error_free (&error); + return ret; + } + + datum = path_get_datum (path, pathstr); + if (!datum) + return droute_object_does_not_exist_error (message); + + if (get && prop_funcs->get) + { + + DBusMessageIter iter; + + _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr); + + reply = dbus_message_new_method_return (message); + dbus_message_iter_init_append (reply, &iter); + if (!(prop_funcs->get) (&iter, datum)) + { + dbus_message_unref (reply); + reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Get failed"); + } + } + else if (!get && prop_funcs->set) + { + DBusMessageIter iter; + + _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr); + + dbus_message_iter_init (message, &iter); + /* Skip the interface and property name */ + dbus_message_iter_next(&iter); + dbus_message_iter_next(&iter); + (prop_funcs->set) (&iter, datum); + + reply = dbus_message_new_method_return (message); + } +#ifdef DBUS_ERROR_PROPERTY_READ_ONLY + else if (!get) + { + reply = dbus_message_new_error (message, DBUS_ERROR_PROPERTY_READ_ONLY, "Property is read-only"); + } +#endif + else + { + reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Getter or setter unavailable"); + } + + return reply; +} + +static DBusHandlerResult +handle_dbus (DBusConnection *bus, + DBusMessage *message, + const gchar *iface, + const gchar *member, + const gchar *pathstr) +{ + static int id = 1; + char *id_str = (char *) g_malloc(40); + DBusMessage *reply; + + if (strcmp (iface, DBUS_INTERFACE_DBUS) != 0 || + strcmp (member, "Hello") != 0) + { + g_free (id_str); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + /* TODO: Fix this hack (we don't handle wrap-around, for instance) */ + sprintf (id_str, ":1.%d", id++); + reply = dbus_message_new_method_return (message); + dbus_message_append_args (reply, DBUS_TYPE_STRING, &id_str, DBUS_TYPE_INVALID); + dbus_connection_send (bus, reply, NULL); + dbus_connection_flush (bus); + dbus_message_unref (reply); + g_free (id_str); + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult +handle_properties (DBusConnection *bus, + DBusMessage *message, + DRoutePath *path, + const gchar *iface, + const gchar *member, + const gchar *pathstr) +{ + DBusMessage *reply = NULL; + DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED; + + if (!g_strcmp0(member, "GetAll")) + reply = impl_prop_GetAll (message, path, pathstr); + else if (!g_strcmp0 (member, "Get")) + reply = impl_prop_GetSet (message, path, pathstr, TRUE); + else if (!g_strcmp0 (member, "Set")) + reply = impl_prop_GetSet (message, path, pathstr, FALSE); + else + result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (reply) + { + dbus_connection_send (bus, reply, NULL); + dbus_message_unref (reply); + } + + return result; +} + +/*---------------------------------------------------------------------------*/ + +static const char *introspection_header = +"<?xml version=\"1.0\"?>\n"; + +static const char *introspection_node_element = +"<node name=\"%s\">\n"; + +static const char *introspection_footer = +"</node>"; + +static DBusHandlerResult +handle_introspection (DBusConnection *bus, + DBusMessage *message, + DRoutePath *path, + const gchar *iface, + const gchar *member, + const gchar *pathstr) +{ + GString *output; + gchar *final; + gint i; + + DBusMessage *reply; + + _DROUTE_DEBUG ("DRoute (handle introspection): %s\n", pathstr); + + if (g_strcmp0 (member, "Introspect")) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + output = g_string_new(introspection_header); + + g_string_append_printf(output, introspection_node_element, pathstr); + + if (!path->get_datum || path_get_datum (path, pathstr)) + { + for (i=0; i < path->introspection->len; i++) + { + gchar *introspect = (gchar *) g_ptr_array_index (path->introspection, i); + g_string_append (output, introspect); + } + } + + if (path->introspect_children_cb) + { + gchar *children = (*path->introspect_children_cb) (pathstr, path->introspect_children_data); + if (children) + { + g_string_append (output, children); + g_free (children); + } + } + + g_string_append(output, introspection_footer); + final = g_string_free(output, FALSE); + + reply = dbus_message_new_method_return (message); + if (!reply) + oom (); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &final, + DBUS_TYPE_INVALID); + dbus_connection_send (bus, reply, NULL); + + dbus_message_unref (reply); + g_free(final); + return DBUS_HANDLER_RESULT_HANDLED; +} + +/*---------------------------------------------------------------------------*/ + +static DBusHandlerResult +handle_other (DBusConnection *bus, + DBusMessage *message, + DRoutePath *path, + const gchar *iface, + const gchar *member, + const gchar *pathstr) +{ + gint result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + StrPair pair; + DRouteFunction func; + DBusMessage *reply = NULL; + + void *datum; + + pair.one = iface; + pair.two = member; + + _DROUTE_DEBUG ("DRoute (handle other): %s|%s on %s\n", member, iface, pathstr); + + func = (DRouteFunction) g_hash_table_lookup (path->methods, &pair); + if (func != NULL) + { + datum = path_get_datum (path, pathstr); + if (!datum) + reply = droute_object_does_not_exist_error (message); + else + reply = (func) (bus, message, datum); + + /* All D-Bus method calls must have a reply. + * If one is not provided presume that the caller has already + * sent one. + */ + if (reply) + { + dbus_connection_send (bus, reply, NULL); + dbus_message_unref (reply); + } + result = DBUS_HANDLER_RESULT_HANDLED; + } + + _DROUTE_DEBUG ("DRoute (handle other) (reply): type %d\n", + dbus_message_get_type(reply)); + return result; +} + +/*---------------------------------------------------------------------------*/ + +static DBusHandlerResult +handle_message (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + DRoutePath *path = (DRoutePath *) user_data; + const gchar *iface = dbus_message_get_interface (message); + const gchar *member = dbus_message_get_member (message); + const gint type = dbus_message_get_type (message); + const gchar *pathstr = dbus_message_get_path (message); + + DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + _DROUTE_DEBUG ("DRoute (handle message): %s|%s of type %d on %s\n", member, iface, type, pathstr); + + /* Check for basic reasons not to handle */ + if (type != DBUS_MESSAGE_TYPE_METHOD_CALL || + member == NULL || + iface == NULL) + return result; + + if (!strcmp (pathstr, DBUS_PATH_DBUS)) + result = handle_dbus (bus, message, iface, member, pathstr); + else if (!strcmp (iface, "org.freedesktop.DBus.Properties")) + result = handle_properties (bus, message, path, iface, member, pathstr); + else if (!strcmp (iface, "org.freedesktop.DBus.Introspectable")) + result = handle_introspection (bus, message, path, iface, member, pathstr); + else + result = handle_other (bus, message, path, iface, member, pathstr); +#if 0 + if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) + g_print ("DRoute | Unhandled message: %s|%s of type %d on %s\n", member, iface, type, pathstr); +#endif + + return result; +} + +/*---------------------------------------------------------------------------*/ + +static DBusMessage * +droute_object_does_not_exist_error (DBusMessage *message) +{ + DBusMessage *reply; + gchar *errmsg; + + errmsg= g_strdup_printf ( + "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed as object %s does not exist\n", + dbus_message_get_member (message), + dbus_message_get_signature (message), + dbus_message_get_interface (message), + dbus_message_get_path (message)); +#ifdef DBUS_ERROR_UNKNOWN_OBJECT + reply = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_OBJECT, + errmsg); +#else + reply = dbus_message_new_error (message, + DBUS_ERROR_FAILED, + errmsg); +#endif + g_free (errmsg); + return reply; +} + +/*---------------------------------------------------------------------------*/ + +DBusMessage * +droute_not_yet_handled_error (DBusMessage *message) +{ + DBusMessage *reply; + gchar *errmsg; + + errmsg= g_strdup_printf ( + "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n", + dbus_message_get_member (message), + dbus_message_get_signature (message), + dbus_message_get_interface (message)); + reply = dbus_message_new_error (message, + DBUS_ERROR_UNKNOWN_METHOD, + errmsg); + g_free (errmsg); + return reply; +} + +DBusMessage * +droute_out_of_memory_error (DBusMessage *message) +{ + DBusMessage *reply; + gchar *errmsg; + + errmsg= g_strdup_printf ( + "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed due to lack of memory\n", + dbus_message_get_member (message), + dbus_message_get_signature (message), + dbus_message_get_interface (message)); + reply = dbus_message_new_error (message, + DBUS_ERROR_NO_MEMORY, + errmsg); + g_free (errmsg); + return reply; +} + +DBusMessage * +droute_invalid_arguments_error (DBusMessage *message) +{ + DBusMessage *reply; + gchar *errmsg; + + errmsg= g_strdup_printf ( + "Method \"%s\" with signature \"%s\" on interface \"%s\" was supplied with invalid arguments\n", + dbus_message_get_member (message), + dbus_message_get_signature (message), + dbus_message_get_interface (message)); + reply = dbus_message_new_error (message, + DBUS_ERROR_INVALID_ARGS, + errmsg); + g_free (errmsg); + return reply; +} + +void +droute_path_register (DRoutePath *path, DBusConnection *bus) +{ + if (path->prefix) + dbus_connection_register_fallback (bus, path->path, &droute_vtable, path); + else + dbus_connection_register_object_path (bus, path->path, + &droute_vtable, path); +} + +void +droute_path_unregister (DRoutePath *path, DBusConnection *bus) +{ + dbus_connection_unregister_object_path (bus, path->path); +} + +void +droute_context_register (DRouteContext *cnx, DBusConnection *bus) +{ + g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_register, + bus); +} + +void +droute_context_unregister (DRouteContext *cnx, DBusConnection *bus) +{ + g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_unregister, + bus); +} + +void +droute_context_deregister (DRouteContext *cnx, DBusConnection *bus) +{ + g_ptr_array_foreach (cnx->registered_paths, (GFunc) droute_path_unregister, + bus); +} + +void +droute_intercept_dbus (DBusConnection *bus) +{ + dbus_connection_register_object_path (bus, DBUS_PATH_DBUS, + &droute_vtable, NULL); +} + +void +droute_unintercept_dbus (DBusConnection *bus) +{ + dbus_connection_unregister_object_path (bus, DBUS_PATH_DBUS); +} + +/*END------------------------------------------------------------------------*/ diff --git a/droute/droute.h b/droute/droute.h new file mode 100644 index 0000000..b19dec6 --- /dev/null +++ b/droute/droute.h @@ -0,0 +1,113 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2008 Novell, Inc. + * Copyright 2008 Codethink Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _DROUTE_H +#define _DROUTE_H + +#include <dbus/dbus.h> +#include <glib.h> + +#include <droute/droute-variant.h> + + +typedef DBusMessage *(*DRouteFunction) (DBusConnection *, DBusMessage *, void *); +typedef dbus_bool_t (*DRoutePropertyFunction) (DBusMessageIter *, void *); +typedef gchar *(*DRouteIntrospectChildrenFunction) (const char *, void *); + +typedef void *(*DRouteGetDatumFunction) (const char *, void *); + +typedef struct _DRouteMethod DRouteMethod; +struct _DRouteMethod +{ + DRouteFunction func; + const char *name; +}; + +typedef struct _DRouteProperty DRouteProperty; +struct _DRouteProperty +{ + DRoutePropertyFunction get; + DRoutePropertyFunction set; + const char *name; +}; + +/*---------------------------------------------------------------------------*/ + +typedef struct _DRouteContext DRouteContext; + +typedef struct _DRoutePath DRoutePath; + +/*---------------------------------------------------------------------------*/ + +DRouteContext * +droute_new (); + +void +droute_free (DRouteContext *cnx); + +DRoutePath * +droute_add_one (DRouteContext *cnx, + const char *path, + const void *data); + +DRoutePath * +droute_add_many (DRouteContext *cnx, + const char *path, + const void *data, + DRouteIntrospectChildrenFunction introspect_children_cb, + void *introspect_children_data, + const DRouteGetDatumFunction get_datum); + +void +droute_path_add_interface (DRoutePath *path, + const char *name, + const char *introspect, + const DRouteMethod *methods, + const DRouteProperty *properties); + +DBusMessage * +droute_not_yet_handled_error (DBusMessage *message); + +DBusMessage * +droute_invalid_arguments_error (DBusMessage *message); + +DBusMessage * +droute_out_of_memory_error (DBusMessage *message); + +void +droute_path_register (DRoutePath *path, DBusConnection *bus); + +void +droute_path_unregister (DRoutePath *path, DBusConnection *bus); + +void +droute_context_register (DRouteContext *cnx, DBusConnection *bus); + +void +droute_context_unregister (DRouteContext *cnx, DBusConnection *bus); + +void +droute_intercept_dbus (DBusConnection *connection); + +void +droute_unintercept_dbus (DBusConnection *connection); +#endif /* _DROUTE_H */ diff --git a/packaging/at-spi2-atk.manifest b/packaging/at-spi2-atk.manifest new file mode 100644 index 0000000..017d22d --- /dev/null +++ b/packaging/at-spi2-atk.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_"/> + </request> +</manifest> diff --git a/packaging/at-spi2-atk.spec b/packaging/at-spi2-atk.spec new file mode 100644 index 0000000..8ce313a --- /dev/null +++ b/packaging/at-spi2-atk.spec @@ -0,0 +1,77 @@ +Name: at-spi2-atk +Version: 2.12.0 +Release: 1 +Summary: Assistive Technology Service Provider Interface - GTK+ module +License: LGPL-2.0+ +Group: System/Libraries +Url: http://www.gnome.org/ +Source: http://download.gnome.org/sources/at-spi2-atk/2.12/%{name}-%{version}.tar.xz +Source1001: %{name}.manifest +Requires: at-spi2-core +BuildRequires: dbus-devel +BuildRequires: glib2-devel +BuildRequires: atk-devel +BuildRequires: at-spi2-core-devel +BuildRequires: gettext + +%description +AT-SPI is a general interface for applications to make use of the +accessibility toolkit. This version is based on dbus not ORBIT / CORBA. + +%package -n libatk-bridge-2_0-0 +Summary: ATK/D-Bus bridging library +Group: System/Libraries + +%description -n libatk-bridge-2_0-0 +AT-SPI is a general interface for applications to make use of the +accessibility toolkit. This version is based on dbus. + +The package contains a ATK/D-Bus bridge library. + +%package devel +Summary: Assistive Technology Service Provider Interface - Developent files +Group: System/Libraries +Requires: libatk-bridge-2_0-0 = %{version} + +%description devel +AT-SPI is a general interface for applications to make use of the +accessibility toolkit. This version is based on dbus. + +%prep +%setup -q +cp %{SOURCE1001} . + +%build +%autogen +%__make %{?_smp_flags} + +%install +rm -rf %{buildroot} +%make_install +find %{buildroot} -name '*.la' -or -name '*.a' | xargs rm -f +find %{buildroot} -name '*.desktop' | xargs rm -f + +%clean +rm -rf %{buildroot} + +%post -n libatk-bridge-2_0-0 -p /sbin/ldconfig + +%postun -n libatk-bridge-2_0-0 -p /sbin/ldconfig + +%files -n libatk-bridge-2_0-0 +%manifest %{name}.manifest +%defattr(-,root,root) +%{_libdir}/libatk-bridge-2.0.so.* +%{_libdir}/gtk-2.0/modules/libatk-bridge.so + +%files devel +%manifest %{name}.manifest +%defattr(-,root,root) +%{_includedir}/at-spi2-atk/2.0/atk-bridge.h +%{_libdir}/libatk-bridge-2.0.so +%{_libdir}/pkgconfig/atk-bridge-2.0.pc + +%changelog +* Mon Mar 25 2013 tomasz.duszynski@comarch.com +- Initial packaging of at-spi2-atk (2.5.92) + diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..741f850 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = diff --git a/tests/cspi/Makefile.am b/tests/cspi/Makefile.am new file mode 100644 index 0000000..b5b8d05 --- /dev/null +++ b/tests/cspi/Makefile.am @@ -0,0 +1,19 @@ +noinst_PROGRAMS = key-listener-test keysynth-test simple-at test-simple + +key_listener_test_SOURCES = key-listener-test.c +keysynth_test_SOURCES = keysynth-test.c +simple_at_SOURCES = simple-at.c +test_simple_SOURCES = test-simple.c + +INCLUDES = -I$(top_srcdir) \ + -I$(top_builddir) \ + $(DBUS_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GTK_CFLAGS) \ + $(ATK_CFLAGS) \ + $(DBIND_CFLAGS) \ + $(DEBUG_CFLAGS) + +LDADD = $(top_builddir)/common/libspicommon.la \ + $(top_builddir)/cspi/libcspi.la \ + $(TESTS_LIBS) $(XINPUT_LIBS) $(ATK_LIBS) $(GTK_LIBS) $(DBIND_LIBS) @EXTRA_SOCKET_LIBS@ diff --git a/tests/cspi/key-listener-test.c b/tests/cspi/key-listener-test.c new file mode 100644 index 0000000..b212a21 --- /dev/null +++ b/tests/cspi/key-listener-test.c @@ -0,0 +1,201 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/un.h> +#include "../../cspi/spi.h" + +static SPIBoolean report_command_key_event (const AccessibleKeystroke *stroke, void *user_data); +static SPIBoolean report_ordinary_key_event (const AccessibleKeystroke *stroke, void *user_data); +static SPIBoolean report_synchronous_key_event (const AccessibleKeystroke *stroke, void *user_data); +static SPIBoolean report_tab_key_event (const AccessibleKeystroke *stroke, void *user_data); +static SPIBoolean report_all_key_event (const AccessibleKeystroke *stroke, void *user_data); + +static AccessibleKeystrokeListener *command_key_listener; +static AccessibleKeystrokeListener *ordinary_key_listener; +static AccessibleKeystrokeListener *synchronous_key_listener; +static AccessibleKeystrokeListener *tab_key_listener; +static AccessibleKeystrokeListener *all_key_listener; +static AccessibleKeySet *command_keyset; +static AccessibleKeySet *async_keyset; +static AccessibleKeySet *sync_keyset; +static AccessibleKeySet *tab_keyset; + +int +main (int argc, char **argv) +{ + const char *tab_strings[1] = {"Tab"}; + short keycodes[] = {65, 64, 23}; + SPIBoolean retval = FALSE; + + SPI_init (); + + /* prepare the keyboard snoopers */ + command_key_listener = SPI_createAccessibleKeystrokeListener (report_command_key_event, NULL); + ordinary_key_listener = SPI_createAccessibleKeystrokeListener (report_ordinary_key_event, NULL); + synchronous_key_listener = SPI_createAccessibleKeystrokeListener (report_synchronous_key_event, NULL); + tab_key_listener = SPI_createAccessibleKeystrokeListener (report_tab_key_event, NULL); + all_key_listener = SPI_createAccessibleKeystrokeListener (report_all_key_event, NULL); + + command_keyset = SPI_createAccessibleKeySet (1, "q", NULL, NULL); + async_keyset = SPI_createAccessibleKeySet (3, NULL, keycodes, NULL); + sync_keyset = SPI_createAccessibleKeySet (3, "def", NULL, NULL); + tab_keyset = SPI_createAccessibleKeySet (1, NULL, NULL, tab_strings); + retval = SPI_registerAccessibleKeystrokeListener(command_key_listener, + command_keyset, + SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL, + (unsigned long) ( SPI_KEY_PRESSED ), + SPI_KEYLISTENER_ALL_WINDOWS); + fprintf (stderr, "Command key registry: result %s\n", retval ? "succeeded" : + "failed"); + retval = SPI_registerAccessibleKeystrokeListener(ordinary_key_listener, + async_keyset, + SPI_KEYMASK_UNMODIFIED, + (unsigned long) ( SPI_KEY_PRESSED | SPI_KEY_RELEASED ), + SPI_KEYLISTENER_NOSYNC); + + retval = SPI_registerAccessibleKeystrokeListener(synchronous_key_listener, + sync_keyset, + SPI_KEYMASK_UNMODIFIED, + (unsigned long) ( SPI_KEY_PRESSED | SPI_KEY_RELEASED ), + SPI_KEYLISTENER_CANCONSUME); + + retval = SPI_registerAccessibleKeystrokeListener(tab_key_listener, + tab_keyset, + SPI_KEYMASK_ALT, + (unsigned long) ( SPI_KEY_PRESSED | SPI_KEY_RELEASED ), + SPI_KEYLISTENER_ALL_WINDOWS); + fprintf (stderr, "tab listener registry: %s\n", retval ? "succeeded" : "failed"); + + retval = SPI_registerAccessibleKeystrokeListener(all_key_listener, + SPI_KEYSET_ALL_KEYS, + SPI_KEYMASK_CONTROL | SPI_KEYMASK_SHIFT, + (unsigned long) ( SPI_KEY_PRESSED | SPI_KEY_RELEASED ), + SPI_KEYLISTENER_ALL_WINDOWS); + + fprintf (stderr, "all key registry: %s\n", retval ? "succeeded" : "failed" ); + + SPI_registerAccessibleKeystrokeListener(all_key_listener, + SPI_KEYSET_ALL_KEYS, + SPI_KEYMASK_SHIFT, + (unsigned long) ( SPI_KEY_PRESSED ), + SPI_KEYLISTENER_NOSYNC | SPI_KEYLISTENER_CANCONSUME); + + SPI_event_main (); + + putenv ("AT_BRIDGE_SHUTDOWN=1"); + + return SPI_exit (); +} + +static void +simple_at_exit (void) +{ + SPI_deregisterAccessibleKeystrokeListener (command_key_listener, SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL); + AccessibleKeystrokeListener_unref (command_key_listener); + SPI_freeAccessibleKeySet (command_keyset); + +/* + SPI_deregisterAccessibleKeystrokeListener (ordinary_key_listener, SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL); */ + AccessibleKeystrokeListener_unref (ordinary_key_listener); + SPI_freeAccessibleKeySet (async_keyset); + +/* SPI_deregisterAccessibleKeystrokeListener (synchronous_key_listener, SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL); */ + AccessibleKeystrokeListener_unref (synchronous_key_listener); + SPI_freeAccessibleKeySet (sync_keyset); + + SPI_deregisterAccessibleKeystrokeListener (tab_key_listener, SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL); + AccessibleKeystrokeListener_unref (tab_key_listener); + SPI_freeAccessibleKeySet (tab_keyset); + + SPI_event_quit (); +} + +static SPIBoolean +is_command_key (const AccessibleKeystroke *key) +{ + switch (key->keyID) + { + case 'Q': + case 'q': + simple_at_exit(); + return TRUE; /* not reached */ + default: + return FALSE; + } +} + +static void +print_key_event (const AccessibleKeystroke *key, char *prefix) +{ + fprintf (stderr, "%s KeyEvent %s%c (keycode %d); string=%s; time=%lx\n", + prefix, + (key->modifiers & SPI_KEYMASK_ALT)?"Alt-":"", + ((key->modifiers & SPI_KEYMASK_SHIFT)^(key->modifiers & SPI_KEYMASK_SHIFTLOCK))? + (char) toupper((int) key->keyID) : (char) tolower((int) key->keyID), + (int) key->keycode, + key->keystring, + (long int) key->timestamp); +} + +static SPIBoolean +report_command_key_event (const AccessibleKeystroke *key, void *user_data) +{ + print_key_event (key, "command"); + return is_command_key (key); +} + +static SPIBoolean +report_ordinary_key_event (const AccessibleKeystroke *key, void *user_data) +{ + print_key_event (key, "ordinary"); + return FALSE; +} + +static SPIBoolean +report_synchronous_key_event (const AccessibleKeystroke *key, void *user_data) +{ + /* consume 'd' key, let others pass through */ + print_key_event (key, "synchronous (consumable) "); + return ( key->keyID == 'd' ) ? TRUE : FALSE; +} + +static SPIBoolean +report_tab_key_event (const AccessibleKeystroke *key, void *user_data) +{ + print_key_event (key, "[TAB]"); + return FALSE; +} + +static SPIBoolean +report_all_key_event (const AccessibleKeystroke *key, void *user_data) +{ + fprintf(stderr, "(%ld)", key->keyID); + return FALSE; +} + diff --git a/tests/cspi/keysynth-test.c b/tests/cspi/keysynth-test.c new file mode 100644 index 0000000..73aa240 --- /dev/null +++ b/tests/cspi/keysynth-test.c @@ -0,0 +1,65 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <unistd.h> +#include <stdlib.h> +#include "../../cspi/spi-private.h" /* A hack for now */ +#include <glib-object.h> +#include <gtk/gtk.h> +#include <atk/atk.h> +#include <atk/atknoopobject.h> +#include "common/spi-dbus.h" + +typedef struct { + long int val; + char *string; + AccessibleKeySynthType type; +} TextTest; + +static TextTest text[] = { + {65, NULL, SPI_KEY_PRESSRELEASE}, + {64, NULL, SPI_KEY_SYM}, + {0, "--hello!", SPI_KEY_STRING}, + {0, "StudlyCaps!", SPI_KEY_STRING} +}; + +static void +test_key_synthesis (void) +{ + int i; + for (i = 0; i < G_N_ELEMENTS (text); ++i) { + SPI_generateKeyboardEvent (text[i].val, text[i].string, text[i].type); + } +} + +int +main (int argc, char **argv) +{ + gtk_init (&argc, &argv); + SPI_init (); + + test_key_synthesis (); + + return SPI_exit (); +} + diff --git a/tests/cspi/simple-at.c b/tests/cspi/simple-at.c new file mode 100644 index 0000000..23cb0ec --- /dev/null +++ b/tests/cspi/simple-at.c @@ -0,0 +1,615 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001, 2002 Sun Microsystems Inc., + * Copyright 2001, 2002 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/un.h> +#undef MAGNIFIER_ENABLED +#include "../../cspi/spi-private.h" /* A hack for now */ + +#define PRINT_TREE + +static void report_focus_event (const AccessibleEvent *event, void *user_data); +static void report_generic_event (const AccessibleEvent *event, void *user_data); +static void report_window_event (const AccessibleEvent *event, void *user_data); +static void report_text_event (const AccessibleEvent *event, void *user_data); +static void report_button_press (const AccessibleEvent *event, void *user_data); +static void check_property_change (const AccessibleEvent *event, void *user_data); +static SPIBoolean report_command_key_event (const AccessibleKeystroke *stroke, void *user_data); +static SPIBoolean report_ordinary_key_event (const AccessibleKeystroke *stroke, void *user_data); +static void get_environment_vars (void); + +static int _festival_init (void); +static void _festival_say (const char *text, const char *voice, SPIBoolean shutup); +static void _festival_write (const char *buff, int fd); + +#ifdef PRINT_TREE +static void print_accessible_tree (Accessible *accessible, char *prefix); +#endif + +#ifdef MAGNIFIER_ENABLED +static SPIBoolean use_magnifier = FALSE; +#endif + +static SPIBoolean use_festival = FALSE; +static SPIBoolean festival_chatty = FALSE; +static SPIBoolean name_changed = FALSE; + +static AccessibleEventListener *focus_listener; +static AccessibleEventListener *property_listener; +static AccessibleEventListener *generic_listener; +static AccessibleEventListener *window_listener; +static AccessibleEventListener *button_listener; +static AccessibleEventListener *text_listener; +static AccessibleKeystrokeListener *command_key_listener; +static AccessibleKeystrokeListener *ordinary_key_listener; +static AccessibleKeySet *command_keyset; + +int +main (int argc, char **argv) +{ + int i, j; + int n_desktops; + int n_apps; + char *s; + Accessible *desktop; + Accessible *application; + const char *modules; + + if ((argc > 1) && (!strncmp (argv[1], "-h", 2))) + { + printf ("Usage: simple-at\n"); + printf ("\tEnvironment variables used:\n\t\tFESTIVAL\n\t\tMAGNIFIER\n\t\tFESTIVAL_CHATTY\n"); + exit (0); + } + + modules = g_getenv ("GTK_MODULES"); + if (!modules || modules [0] == '\0') + { + putenv ("GTK_MODULES="); + } + modules = NULL; + + SPI_init (); + + focus_listener = SPI_createAccessibleEventListener (report_focus_event, NULL); + property_listener = SPI_createAccessibleEventListener (check_property_change, NULL); + generic_listener = SPI_createAccessibleEventListener (report_generic_event, NULL); + window_listener = SPI_createAccessibleEventListener (report_window_event, NULL); + text_listener = SPI_createAccessibleEventListener (report_text_event, NULL); + button_listener = SPI_createAccessibleEventListener (report_button_press, NULL); + SPI_registerGlobalEventListener (focus_listener, "focus:"); + SPI_registerGlobalEventListener (property_listener, "object:property-change"); +/* :accessible-selection"); */ + SPI_registerGlobalEventListener (property_listener, "object:property-change:accessible-name"); + SPI_registerGlobalEventListener (generic_listener, "object:selection-changed"); + SPI_registerGlobalEventListener (generic_listener, "object:children-changed"); + SPI_registerGlobalEventListener (generic_listener, "object:visible-data-changed"); + SPI_registerGlobalEventListener (generic_listener, "object:text-selection-changed"); + SPI_registerGlobalEventListener (text_listener, "object:text-caret-moved"); + SPI_registerGlobalEventListener (text_listener, "object:text-changed"); + SPI_registerGlobalEventListener (button_listener, "Gtk:GtkWidget:button-press-event"); + SPI_registerGlobalEventListener (window_listener, "window:minimize"); + SPI_registerGlobalEventListener (window_listener, "window:activate"); + n_desktops = SPI_getDesktopCount (); + + for (i=0; i<n_desktops; ++i) + { + desktop = SPI_getDesktop (i); + s = Accessible_getName (desktop); + fprintf (stderr, "desktop %d name: %s\n", i, s); + SPI_freeString (s); + n_apps = Accessible_getChildCount (desktop); + for (j=0; j<n_apps; ++j) + { + application = Accessible_getChildAtIndex (desktop, j); + s = Accessible_getName (application); + fprintf (stderr, "app %d name: %s\n", j, s ? s : "(nil)"); +#ifdef PRINT_TREE + print_accessible_tree (application, "*"); +#endif + SPI_freeString (s); + Accessible_unref (application); + } + Accessible_unref (desktop); + } + + /* prepare the keyboard snoopers */ + command_key_listener = SPI_createAccessibleKeystrokeListener (report_command_key_event, NULL); + ordinary_key_listener = SPI_createAccessibleKeystrokeListener (report_ordinary_key_event, NULL); + + command_keyset = SPI_createAccessibleKeySet (11, "qmf23456789", NULL, NULL); + + /* will listen only to Control-Alt-q KeyPress events */ + SPI_registerAccessibleKeystrokeListener(command_key_listener, + command_keyset, + SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL, + (unsigned long) ( SPI_KEY_PRESSED ), + SPI_KEYLISTENER_ALL_WINDOWS); + + /* will listen only to CAPSLOCK key events, both press and release */ + SPI_registerAccessibleKeystrokeListener(ordinary_key_listener, + (AccessibleKeySet *) SPI_KEYSET_ALL_KEYS, + SPI_KEYMASK_SHIFTLOCK, + (unsigned long) ( SPI_KEY_PRESSED | SPI_KEY_RELEASED ), + SPI_KEYLISTENER_NOSYNC); + + get_environment_vars (); + + SPI_event_main (); + + putenv ("AT_BRIDGE_SHUTDOWN=1"); + + return SPI_exit (); +} + +static void +get_environment_vars (void) +{ + if (g_getenv ("FESTIVAL")) + { + fprintf (stderr, "Using festival\n"); + use_festival = TRUE; + if (g_getenv ("FESTIVAL_CHATTY")) + { + festival_chatty = TRUE; + } + } +#ifdef MAGNIFIER_ENABLED + if (g_getenv ("MAGNIFIER")) + { + fprintf (stderr, "Using magnifier\n"); + use_magnifier = TRUE; + } + else + { + fprintf (stderr, "Not using magnifier\n"); + } +#endif + + if (!use_festival) + { + fprintf (stderr, "No speech output\n"); + } +} + +#ifdef PRINT_TREE +static void +print_accessible_tree (Accessible *accessible, char *prefix) +{ + int n_children; + int i; + char *name; + char *role_name; + char *parent_name = NULL; + char *parent_role = NULL; + char child_prefix[100]; + Accessible *child; + Accessible *parent; + + strncpy (child_prefix, prefix, 98); + strcat (child_prefix, "*"); + parent = Accessible_getParent (accessible); + if (parent) + { + parent_name = Accessible_getName (parent); + parent_role = Accessible_getRoleName (parent); + Accessible_unref (parent); + } + name = Accessible_getName (accessible); + role_name = Accessible_getRoleName (accessible); + fprintf (stdout, "%sAccessible [%s] \"%s\"; parent [%s] %s.\n", + prefix, role_name, name ? name : "(nil)", + parent_role ? parent_role : "(nil)", + parent_name ? parent_name : "(nil)"); + SPI_freeString (name); + SPI_freeString (role_name); + SPI_freeString (parent_name); + SPI_freeString (parent_role); + n_children = Accessible_getChildCount (accessible); + for (i = 0; i < n_children; ++i) + { + child = Accessible_getChildAtIndex (accessible, i); + print_accessible_tree (child, child_prefix); + Accessible_unref (child); + } +} +#endif + +void +report_focussed_accessible (Accessible *obj, SPIBoolean shutup_previous_speech) +{ + char *s; + int len; + long x, y, width, height; + /* hack for GUADEC demo, to make sure name changes are spoken */ + shutup_previous_speech = (shutup_previous_speech && !name_changed); + + if (use_festival) + { + if (festival_chatty) + { + s = Accessible_getRoleName (obj); + _festival_say (s, "voice_don_diphone", shutup_previous_speech); + SPI_freeString (s); + } + fprintf (stderr, "getting Name\n"); + s = Accessible_getName (obj); + _festival_say (s, "voice_kal_diphone", + shutup_previous_speech || festival_chatty); + SPI_freeString (s); + } + + if (Accessible_isComponent (obj)) + { + AccessibleComponent *component = Accessible_getComponent (obj); + AccessibleComponent_getExtents (component, &x, &y, &width, &height, + SPI_COORD_TYPE_SCREEN); + fprintf (stderr, "Bounding box: (%ld, %ld) ; (%ld, %ld)\n", + x, y, x+width, y+height); + if (Accessible_isText (obj)) + { + long x0, y0, xN, yN, w0, h0, wN, hN, nchars; + AccessibleText *text = Accessible_getText (obj); + nchars = AccessibleText_getCharacterCount (text); + if (nchars > 0) + { + AccessibleText_getCharacterExtents (text, 0, &x0, &y0, &w0, &h0, + SPI_COORD_TYPE_SCREEN); + AccessibleText_getCharacterExtents (text, nchars-1, &xN, &yN, &wN, &hN, + SPI_COORD_TYPE_SCREEN); + x = MIN (x0, xN); + width = MAX (x0 + w0, xN + wN) - x; + fprintf (stderr, "Text bounding box: (%ld, %ld) ; (%ld, %ld)\n", + x, y, x+width, y+height); + } + } +#ifdef MAGNIFIER_ENABLED + if (use_magnifier) { + magnifier_set_roi ((short) 0, x, y, width, height); + } +#endif + } + + + if (Accessible_isValue (obj)) + { + AccessibleValue *value = Accessible_getValue (obj); + fprintf (stderr, "Current value = %f, min = %f; max = %f\n", + AccessibleValue_getCurrentValue (value), + AccessibleValue_getMinimumValue (value), + AccessibleValue_getMaximumValue (value)); + } + /* if this is a text object, speak the first sentence. */ + + if (Accessible_isText(obj)) + + { + AccessibleText *text_interface; + long start_offset, end_offset; + char *first_sentence = "empty"; + text_interface = Accessible_getText (obj); + first_sentence = AccessibleText_getTextAtOffset ( + text_interface, (long) 0, SPI_TEXT_BOUNDARY_SENTENCE_START, &start_offset, &end_offset); + if (first_sentence && use_festival) + { + _festival_say(first_sentence, "voice_don_diphone", FALSE); + SPI_freeString (first_sentence); + } + len = AccessibleText_getCharacterCount (text_interface); + s = AccessibleText_getText (text_interface, 0, len); + fprintf (stderr, "done reporting on focussed object, text=%s\n", s); + } +} + +void +report_focus_event (const AccessibleEvent *event, void *user_data) +{ + char *s; + + g_return_if_fail (event->source != NULL); + s = Accessible_getName (event->source); + if (s) + { + fprintf (stderr, "%s event from %s\n", event->type, s); + SPI_freeString (s); + report_focussed_accessible (event->source, TRUE); + } + Accessible_getParent (event->source); + name_changed = FALSE; +} + +void +report_generic_event (const AccessibleEvent *event, void *user_data) +{ + fprintf (stderr, "%s event received\n", event->type); +} + +void +report_window_event (const AccessibleEvent *event, void *user_data) +{ + fprintf (stderr, "%s event received\n", event->type); + if (!strcmp (event->type, "window:activate")) + { + print_accessible_tree (event->source, "window"); + } +} + +void +report_text_event (const AccessibleEvent *event, void *user_data) +{ + AccessibleText *text = Accessible_getText (event->source); + fprintf (stderr, "%s event received\n", event->type); +#ifdef MAGNIFIER_ENABLED + if (use_magnifier && strcmp (event->type, "object:text-changed")) + { + long offset = AccessibleText_getCaretOffset (text); + long x, y, w, h; + fprintf (stderr, "offset %d\n", (int) offset); + AccessibleText_getCharacterExtents (text, offset, &x, &y, &w, &h, + SPI_COORD_TYPE_SCREEN); + fprintf (stderr, "new roi %d %d %d %d\n", (int) x, (int) y, (int) w, (int) h); + magnifier_set_roi ((short) 0, x, y, w, h); + } +#endif + if (!strcmp (event->type, "object:text-changed")) + { + long start, end; + char *new_text = AccessibleText_getTextAtOffset (text, (long) event->detail1, SPI_TEXT_BOUNDARY_WORD_START, &start, &end); + _festival_say (new_text, "voice_kal_diphone", FALSE); + fprintf (stderr, "text changed: %s", new_text ? new_text : ""); + SPI_freeString (new_text); + } + else + { + long start, end; + char *word_text = AccessibleText_getTextAtOffset (text, (long) event->detail1, SPI_TEXT_BOUNDARY_WORD_START, &start, &end); + char *sentence_text = AccessibleText_getTextAtOffset (text, (long) event->detail1, SPI_TEXT_BOUNDARY_SENTENCE_START, &start, &end); + fprintf (stderr, "text changed: word %s; sentence %s at %ld", + (word_text ? word_text : ""), + (sentence_text ? sentence_text : ""), + event->detail1); + if (word_text) SPI_freeString (word_text); + if (sentence_text) SPI_freeString (sentence_text); + } +} + +void +report_button_press (const AccessibleEvent *event, void *user_data) +{ + char *s; + + g_return_if_fail (event->source != NULL); + + s = Accessible_getName (event->source); + + fprintf (stderr, "%s event from %s\n", event->type, s); + SPI_freeString (s); + s = Accessible_getDescription (event->source); + fprintf (stderr, "Object description %s\n", s); + SPI_freeString (s); +} + +void +check_property_change (const AccessibleEvent *event, void *user_data) +{ + AccessibleSelection *selection = Accessible_getSelection (event->source); + int n_selections; + int i; + char *s; + fprintf (stderr, "property change event!\n"); + if (selection) + { + n_selections = (int) AccessibleSelection_getNSelectedChildren (selection); + s = Accessible_getName (event->source); + fprintf (stderr, "(Property) %s event from %s, %d selected children\n", + event->type, s, n_selections); + SPI_freeString (s); + /* for now, speak entire selection set */ + for (i=0; i<n_selections; ++i) + { + Accessible *obj = AccessibleSelection_getSelectedChild (selection, (long) i); + g_return_if_fail (obj); + s = Accessible_getName (obj); + fprintf (stderr, "Child %d, name=%s\n", i, s); + SPI_freeString (s); + report_focussed_accessible (obj, i==0); + } + } + else if (!strcmp (event->type, "object:property-change:accessible-name")) + { + name_changed = TRUE; + report_focussed_accessible (event->source, TRUE); + } + else + { + fprintf (stderr, "Property change %s received\n", event->type); + } +} + +static void +simple_at_exit (void) +{ + SPI_deregisterGlobalEventListenerAll (focus_listener); + AccessibleEventListener_unref (focus_listener); + + SPI_deregisterGlobalEventListenerAll (property_listener); + AccessibleEventListener_unref (property_listener); + + SPI_deregisterGlobalEventListenerAll (generic_listener); + AccessibleEventListener_unref (generic_listener); + + SPI_deregisterGlobalEventListenerAll (text_listener); + AccessibleEventListener_unref (text_listener); + + SPI_deregisterGlobalEventListenerAll (button_listener); + AccessibleEventListener_unref (button_listener); + + SPI_deregisterAccessibleKeystrokeListener (command_key_listener, SPI_KEYMASK_ALT | SPI_KEYMASK_CONTROL); + AccessibleKeystrokeListener_unref (command_key_listener); + SPI_freeAccessibleKeySet (command_keyset); + + SPI_deregisterAccessibleKeystrokeListener (ordinary_key_listener, SPI_KEYMASK_SHIFTLOCK); + AccessibleKeystrokeListener_unref (ordinary_key_listener); + + SPI_event_quit (); +} + +static SPIBoolean +is_command_key (const AccessibleKeystroke *key) +{ + switch (key->keyID) + { + case 'Q': + case 'q': + simple_at_exit(); + return TRUE; /* not reached */ +#ifdef MAGNIFIER_ENABLED + case 'M': + case 'm': + use_magnifier = ! use_magnifier; + fprintf (stderr, "%ssing magnifier\n", use_magnifier ? "U" : "Not u"); + return TRUE; +#endif + case 'F': + case 'f': + use_festival = ! use_festival; + fprintf (stderr, "%speech output\n", use_festival ? "S" : "No s"); + return TRUE; + default: + return FALSE; + } +} + +static SPIBoolean +report_command_key_event (const AccessibleKeystroke *key, void *user_data) +{ + fprintf (stderr, "Command KeyEvent %s%c (keycode %d); string=%s; time=%lx\n", + (key->modifiers & SPI_KEYMASK_ALT)?"Alt-":"", + ((key->modifiers & SPI_KEYMASK_SHIFT)^(key->modifiers & SPI_KEYMASK_SHIFTLOCK))? + (char) toupper((int) key->keyID) : (char) tolower((int) key->keyID), + (int) key->keycode, + key->keystring, + (long int) key->timestamp); + return is_command_key (key); +} + + +static SPIBoolean +report_ordinary_key_event (const AccessibleKeystroke *key, void *user_data) +{ + fprintf (stderr, "Received key event:\tsym %ld\n\tmods %x\n\tcode %d\n\tstring=\'%s\'\n\ttime %lx\n", + (long) key->keyID, + (unsigned int) key->modifiers, + (int) key->keycode, + key->keystring, + (long int) key->timestamp); + return FALSE; +} + +static int +_festival_init (void) +{ + int fd; + struct sockaddr_in name; + int tries = 2; + + name.sin_family = AF_INET; + name.sin_port = htons (1314); + name.sin_addr.s_addr = htonl(INADDR_ANY); + fd = socket (PF_INET, SOCK_STREAM, 0); + + while (connect(fd, (struct sockaddr *) &name, sizeof (name)) < 0) { + if (!tries--) { + perror ("connect"); + return -1; + } + } + + _festival_write ("(audio_mode'async)\n", fd); + _festival_write ("(Parameter.set 'Duration_Model 'Tree_ZScore)\n", fd); + _festival_write ("(Parameter.set 'Duration_Stretch 0.75)\n", fd); + return fd; +} + +static void +_festival_say (const char *text, const char *voice, SPIBoolean shutup) +{ + static int fd = 0; + gchar *quoted; + gchar *p; + gchar prefix[50]; + static gchar voice_spec[32]; + + if (!fd) + { + fd = _festival_init (); + } + + fprintf (stderr, "saying text: %s\n", text); + + quoted = g_malloc(64+strlen(text)*2); + + sprintf (prefix, "(SayText \""); + + strncpy(quoted, prefix, 10); + p = quoted+strlen(prefix); + while (*text) { + if ( *text == '\\' || *text == '"' ) + *p = '\\'; + *p++ = *text++; + } + *p++ = '"'; + *p++ = ')'; + *p++ = '\n'; + *p = 0; + + if (shutup) _festival_write ("(audio_mode'shutup)\n", fd); + if (voice && (strncmp (voice, (char *) (voice_spec+1), strlen(voice)))) + { + snprintf (voice_spec, 32, "(%s)\n", voice); + _festival_write (voice_spec, fd); + _festival_write ("(Parameter.set 'Duration_Model 'Tree_ZScore)\n", fd); + _festival_write ("(Parameter.set 'Duration_Stretch 0.75)\n", fd); + } + + _festival_write (quoted, fd); + + g_free(quoted); +} + +static void +_festival_write (const gchar *command_string, int fd) +{ + fprintf(stderr, command_string); + if (fd < 0) { + perror("socket"); + return; + } + write(fd, command_string, strlen(command_string)); +} + diff --git a/tests/cspi/test-simple.c b/tests/cspi/test-simple.c new file mode 100644 index 0000000..62bcb31 --- /dev/null +++ b/tests/cspi/test-simple.c @@ -0,0 +1,816 @@ +/* + * test-simple.c: A set of simple regression tests + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001 Ximian, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * ******** Do not copy this code as an example ********* + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <gtk/gtk.h> +#include <cspi/spi.h> +#include <cspi/spi-private.h> + +#include "dbus/dbus.h" + +/* Known bugs */ +#define WHOLE_STRING -1 + +static void validate_accessible (Accessible *accessible, + gboolean has_parent, + gboolean recurse_down); + +#define WINDOW_MAGIC 0x123456a +#define TEST_STRING_A "A test string" +#define TEST_STRING_A_OBJECT "A_test_string_object" +#define TEST_STRING_B "Another test string" + +static int print_tree_depth = 0; +static gboolean print_tree = FALSE; +static gboolean do_poke = FALSE; +static gboolean key_press_received = FALSE; +static gboolean key_release_received = FALSE; + +typedef struct { + gulong magic; + GtkWidget *window; +} TestWindow; + +static gboolean +focus_me (GtkWidget *widget) +{ + AtkObject *aobject = atk_implementor_ref_accessible ( + ATK_IMPLEMENTOR (widget)); + + /* Force a focus event - even if the WM focused + * us before our at-bridge's idle handler registered + * our interest */ + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); +/* else: FIXME - gtk_widget_grab_focus should send a notify */ + atk_focus_tracker_notify (aobject); + + g_object_unref (G_OBJECT (aobject)); + + return FALSE; +} + +static void +test_window_add_and_show (GtkContainer *container, GtkWidget *widget) +{ + gtk_container_add (container, widget); + gtk_widget_show (widget); +} + +static GtkWidget * +create_tree (void) +{ + GtkWidget *widget; + GtkTreeIter iter; + GtkListStore *store; + GtkTreeViewColumn *column; + + store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, TEST_STRING_A, -1); + column = gtk_tree_view_column_new_with_attributes ("String", + gtk_cell_renderer_text_new (), "text", 0, NULL); + widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column); + + return widget; +} + +static TestWindow * +create_test_window (void) +{ + TestWindow *win = g_new0 (TestWindow, 1); + GtkWidget *widget, *vbox; + AtkObject *obj; + + win->magic = WINDOW_MAGIC; + win->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_widget_show (win->window); + + vbox = gtk_vbox_new (0, 0); + gtk_container_add (GTK_CONTAINER (win->window), vbox); + gtk_widget_show (vbox); + + widget = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (widget), TEST_STRING_A); + obj = gtk_widget_get_accessible (widget); + atk_object_set_name (obj, TEST_STRING_A_OBJECT); + + test_window_add_and_show (GTK_CONTAINER (vbox), widget); + + widget = gtk_button_new_with_label ("_Foobar"); + test_window_add_and_show (GTK_CONTAINER (vbox), widget); + + widget = gtk_hseparator_new (); + test_window_add_and_show (GTK_CONTAINER (vbox), widget); + + widget = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, + GTK_ICON_SIZE_LARGE_TOOLBAR); + test_window_add_and_show (GTK_CONTAINER (vbox), widget); + + widget = g_object_new (GTK_TYPE_HSCALE, NULL); + gtk_range_set_range (GTK_RANGE (widget), 0.0, 100.0); + test_window_add_and_show (GTK_CONTAINER (vbox), widget); + + widget = create_tree (); + test_window_add_and_show (GTK_CONTAINER (vbox), widget); + + g_idle_add ((GSourceFunc) focus_me, win->window); + + return win; +} + +static void +test_window_destroy (TestWindow *win) +{ + gtk_widget_destroy (win->window); + g_free (win); +} + +static void +test_roles (void) +{ + int i; + + fprintf (stderr, "Testing roles...\n"); + for (i = -1; i < 1000; i++) + g_assert (AccessibleRole_getName (i) != NULL); + + g_assert (!strcmp (AccessibleRole_getName (SPI_ROLE_FILE_CHOOSER), "file-chooser")); + g_assert (!strcmp (AccessibleRole_getName (SPI_ROLE_RADIO_BUTTON), "radio-button")); + g_assert (!strcmp (AccessibleRole_getName (SPI_ROLE_TABLE), "table")); + g_assert (!strcmp (AccessibleRole_getName (SPI_ROLE_WINDOW), "window")); +} + +static void +test_action (AccessibleAction *action) +{ + gint n_actions, i; + gchar *s, *sd; + g_assert ((n_actions = AccessibleAction_getNActions (action)) >= 0); + + fprintf (stderr, "Testing actions..."); + for (i = 0; i < n_actions; ++i) + { + s = AccessibleAction_getName (action, i); + g_assert (s); + sd = AccessibleAction_getDescription (action, i); + g_assert (sd); + fprintf (stderr, "%d: %s (%s); ", i, s, sd); + SPI_freeString (s); + SPI_freeString (sd); + g_assert (AccessibleAction_doAction (action, i)); + } + fprintf (stderr, "\n"); +} + +static void +test_desktop (void) +{ + Accessible *desktop; + Accessible *application; + Accessible **list; + + fprintf (stderr, "Testing desktop...\n"); + + g_assert (SPI_getDesktop (-1) == NULL); + desktop = SPI_getDesktop (0); + g_assert (desktop != NULL); + + g_assert ((SPI_getDesktopList (&list)) > 0); + g_assert (list[0] == desktop); + SPI_freeDesktopList (list); + + validate_accessible (desktop, FALSE, FALSE); + + application = Accessible_getChildAtIndex (desktop, 0); + g_assert (application != NULL); + AccessibleApplication_unref (application); + + Accessible_unref (desktop); +} + +static void +test_application (Accessible *application) +{ + char *str; + + fprintf (stderr, "Testing application ...\n"); + g_assert (Accessible_isApplication (application)); + g_assert (Accessible_getApplication (application) == + application); + AccessibleApplication_unref (application); + + str = AccessibleApplication_getToolkitName (application); + g_assert (str != NULL); + g_assert (!strcmp (str, "GAIL")); + SPI_freeString (str); + + str = AccessibleApplication_getLocale (application, LC_MESSAGES); + g_assert (!strcmp (str, setlocale (LC_MESSAGES, NULL))); + SPI_freeString (str); + + str = AccessibleApplication_getVersion (application); + g_assert (str != NULL); + SPI_freeString (str); + + AccessibleApplication_getID (application); +} + +static void +test_editable_text (AccessibleEditableText *etext) +{ + char *str; + AccessibleText *text; + + fprintf (stderr, "Testing editable text ...\n"); + + g_assert (Accessible_isText (etext)); + text = Accessible_getText (etext); + + AccessibleEditableText_setTextContents ( + etext, TEST_STRING_B); + + str = AccessibleText_getText (text, 0, WHOLE_STRING); + g_assert (!strcmp (str, TEST_STRING_B)); + + SPI_freeString (str); + + /* FIXME: lots more editing here */ + + AccessibleEditableText_setTextContents ( + etext, TEST_STRING_A); + + AccessibleText_unref (text); +} + +static void +test_table (AccessibleTable *table) +{ + Accessible *header; + gint index; + gint rows, columns; + + fprintf (stderr, "Testing table ...\n"); + + rows = AccessibleTable_getNRows (table); + g_assert (rows > 0); + + columns = AccessibleTable_getNColumns (table); + g_assert (columns > 0); + + index = AccessibleTable_getIndexAt (table, rows - 1, columns - 1); + + g_assert (AccessibleTable_getRowAtIndex (table, index) == rows - 1); + + g_assert (AccessibleTable_getColumnAtIndex (table, index) == columns - 1); + + g_assert ((header = AccessibleTable_getColumnHeader (table, 0))); + Accessible_unref (header); + + AccessibleTable_isSelected (table, 0, 0); + + /* FIXME: lots more tests */ +} + +static void +test_text (AccessibleText *text) +{ + char *str; + + fprintf (stderr, "Testing text ...\n"); + + g_assert (AccessibleText_getCharacterCount (text) == + strlen (TEST_STRING_A)); + + str = AccessibleText_getText (text, 0, WHOLE_STRING); + g_assert (!strcmp (str, TEST_STRING_A)); + SPI_freeString (str); + + str = AccessibleText_getText (text, 0, 5); + g_assert (!strncmp (str, TEST_STRING_A, 5)); + SPI_freeString (str); + + AccessibleText_setCaretOffset (text, 7); + g_assert (AccessibleText_getCaretOffset (text) == 7); + + /* FIXME: lots more tests - selections etc. etc. */ +} + +static void +test_value (AccessibleValue *value) +{ + float original_value; + + fprintf (stderr, "Testing value ...\n"); + + original_value = AccessibleValue_getCurrentValue (value); + + g_assert (AccessibleValue_getCurrentValue (value) <= + AccessibleValue_getMaximumValue (value)); + + g_assert (AccessibleValue_getCurrentValue (value) >= + AccessibleValue_getMinimumValue (value)); + + AccessibleValue_setCurrentValue (value, + AccessibleValue_getMinimumValue (value)); + + g_assert (AccessibleValue_getCurrentValue (value) == + AccessibleValue_getMinimumValue (value)); + + AccessibleValue_setCurrentValue (value, + AccessibleValue_getMaximumValue (value)); + + g_assert (AccessibleValue_getCurrentValue (value) == + AccessibleValue_getMaximumValue (value)); + + AccessibleValue_setCurrentValue (value, original_value); + + g_assert (AccessibleValue_getCurrentValue (value) == original_value); +} + +static void +test_component (AccessibleComponent *component) +{ + long x, y, width, height; + + fprintf (stderr, "Testing component...\n"); + + AccessibleComponent_getExtents ( + component, &x, &y, &width, &height, SPI_COORD_TYPE_SCREEN); + + AccessibleComponent_getPosition ( + component, &x, &y, SPI_COORD_TYPE_SCREEN); + + AccessibleComponent_getSize (component, &width, &height); + + if (width > 0 && height > 0) { +#ifdef FIXME + Accessible *accessible, *componentb; +#endif + + g_assert (AccessibleComponent_contains ( + component, x, y, SPI_COORD_TYPE_SCREEN)); + + g_assert (AccessibleComponent_contains ( + component, x + width - 1, y, SPI_COORD_TYPE_SCREEN)); + + g_assert (AccessibleComponent_contains ( + component, x + width - 1, y + height - 1, + SPI_COORD_TYPE_SCREEN)); + +#ifdef FIXME + accessible = AccessibleComponent_getAccessibleAtPoint ( + component, x, y, SPI_COORD_TYPE_SCREEN); + + g_assert (Accessible_isComponent (accessible)); + componentb = Accessible_getComponent (accessible); + g_assert (componentb == component); + + AccessibleComponent_unref (componentb); + Accessible_unref (accessible); +#endif + } + + AccessibleComponent_getLayer (component); + AccessibleComponent_getMDIZOrder (component); +/* AccessibleComponent_grabFocus (component); */ +} + +static void +test_image (AccessibleImage *image) +{ + char *desc; + long int x = -1, y = -1, width = -1, height = -1; + + desc = AccessibleImage_getImageDescription (image); + g_assert (desc != NULL); + SPI_freeString (desc); + + AccessibleImage_getImagePosition (image, &x, &y, + SPI_COORD_TYPE_SCREEN); + AccessibleImage_getImageSize (image, &width, &height); + AccessibleImage_getImageExtents (image, &x, &y, &width, &height, + SPI_COORD_TYPE_WINDOW); +} + +static void +validate_tree (Accessible *accessible, + gboolean has_parent, + gboolean recurse_down) +{ + Accessible *parent; + long len, i; + + parent = Accessible_getParent (accessible); + if (has_parent) { + long index; + Accessible *child_at_index; + + g_assert (parent != NULL); + + index = Accessible_getIndexInParent (accessible); + g_assert (index >= 0); + + child_at_index = Accessible_getChildAtIndex (parent, index); + + g_assert (child_at_index == accessible); + + Accessible_unref (child_at_index); + Accessible_unref (parent); + } + + len = Accessible_getChildCount (accessible); + print_tree_depth++; + for (i = 0; i < len; i++) { + Accessible *child; + + child = Accessible_getChildAtIndex (accessible, i); +#ifdef ROPEY + if (!child) + fprintf (stderr, "Unusual - ChildGone at %ld\n", i); + + g_assert (Accessible_getIndexInParent (child) == i); + g_assert (Accessible_getParent (child) == accessible); +#endif + + if (recurse_down && child) + validate_accessible (child, has_parent, recurse_down); + + Accessible_unref (child); + } + print_tree_depth--; +} + +static void +validate_accessible (Accessible *accessible, + gboolean has_parent, + gboolean recurse_down) +{ + Accessible *tmp; + char *name, *descr; + AccessibleRole role; + AccessibleRelation **relations; + char *role_name; + GString *item_str = g_string_new (""); + int i; + + name = Accessible_getName (accessible); + g_assert (name != NULL); + + descr = Accessible_getDescription (accessible); + g_assert (descr != NULL); + + role = Accessible_getRole (accessible); + g_assert (role != SPI_ROLE_INVALID); + role_name = Accessible_getRoleName (accessible); + g_assert (role_name != NULL); + + relations = Accessible_getRelationSet (accessible); + g_assert (relations != NULL); + + for (i = 0; relations [i]; i++) { + AccessibleRelationType type; + int targets; + + fprintf (stderr, "relation %d\n", i); + + type = AccessibleRelation_getRelationType (relations [i]); + g_assert (type != SPI_RELATION_NULL); + + targets = AccessibleRelation_getNTargets (relations [i]); + g_assert (targets != -1); + + AccessibleRelation_unref (relations [i]); + relations [i] = NULL; + } + free (relations); + + if (print_tree) { + int i; + + for (i = 0; i < print_tree_depth; i++) + fputc (' ', stderr); + fputs ("|-> [ ", stderr); + } + + if (Accessible_isAction (accessible)) { + tmp = Accessible_getAction (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "At"); + else + test_action (tmp); + AccessibleAction_unref (tmp); + } + + if (Accessible_isApplication (accessible)) { + tmp = Accessible_getApplication (accessible); + if (print_tree) + fprintf (stderr, "Ap"); + else + test_application (tmp); + AccessibleApplication_unref (tmp); + } + + if (Accessible_isComponent (accessible)) { + tmp = Accessible_getComponent (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Co"); + else + test_component (tmp); + AccessibleComponent_unref (tmp); + } + + if (Accessible_isEditableText (accessible)) { + tmp = Accessible_getEditableText (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Et"); + else + test_editable_text (tmp); + AccessibleEditableText_unref (tmp); + } + + if (Accessible_isHypertext (accessible)) { + tmp = Accessible_getHypertext (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Ht"); + AccessibleHypertext_unref (tmp); + } + + if (Accessible_isImage (accessible)) { + tmp = Accessible_getImage (accessible); + g_assert (tmp != NULL); + if (print_tree) { + char *desc; + + fprintf (stderr, "Im"); + + desc = AccessibleImage_getImageDescription (tmp); + g_string_append_printf ( + item_str, " image descr: '%s'", desc); + SPI_freeString (desc); + } else + test_image (tmp); + + AccessibleImage_unref (tmp); + } + + if (Accessible_isSelection (accessible)) { + tmp = Accessible_getSelection (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Se"); + AccessibleSelection_unref (tmp); + } + + if (Accessible_isTable (accessible)) { + tmp = Accessible_getTable (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Ta"); + else + test_table (tmp); + AccessibleTable_unref (tmp); + } + + if (Accessible_isText (accessible)) { + tmp = Accessible_getText (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Te"); + else { + if (strcmp (name, TEST_STRING_A_OBJECT) == 0) + test_text (tmp); + } + AccessibleText_unref (tmp); + } + + if (Accessible_isValue (accessible)) { + tmp = Accessible_getValue (accessible); + g_assert (tmp != NULL); + if (print_tree) + fprintf (stderr, "Va"); + else + test_value (tmp); + AccessibleValue_unref (tmp); + } + + if (print_tree) + fprintf (stderr, " ] '%s' (%s) - %s: %s\n", + name, descr, role_name, item_str->str); + + SPI_freeString (name); + SPI_freeString (descr); + SPI_freeString (role_name); + g_string_free (item_str, TRUE); + + validate_tree (accessible, has_parent, recurse_down); +} + +static void +test_misc (void) +{ + fprintf (stderr, "Testing misc bits ...\n"); + + g_assert (!Accessible_isComponent (NULL)); + g_assert (Accessible_getComponent (NULL) == NULL); + SPI_freeString (NULL); +} + +static void +global_listener_cb (const AccessibleEvent *event, + void *user_data) +{ + TestWindow *win = user_data; + Accessible *desktop; + AccessibleApplication *application; + + g_assert (win->magic == WINDOW_MAGIC); + g_assert (!strcmp (event->type, "focus:")); + + fprintf (stderr, "Fielded focus event ...\n"); + + if (!do_poke) { + desktop = SPI_getDesktop (0); + application = Accessible_getChildAtIndex (desktop, 0); + g_assert (application != NULL); + Accessible_unref (desktop); + + test_application (application); + + AccessibleApplication_unref (application); + + print_tree = FALSE; + + validate_accessible (event->source, TRUE, TRUE); + + fprintf (stderr, "quitting mainloop.\n"); + gtk_main_quit (); + } + + print_tree = TRUE; + validate_accessible (event->source, TRUE, TRUE); +} + +static SPIBoolean +key_listener_cb (const AccessibleKeystroke *stroke, + void *user_data) +{ + AccessibleKeystroke *s = user_data; + + *s = *stroke; + if (stroke->keystring) s->keystring = g_strdup (stroke->keystring); + + if (s->type == SPI_KEY_PRESSED) + key_press_received = TRUE; + else if (s->type == SPI_KEY_RELEASED) + key_release_received = TRUE; + + return TRUE; +} + + +static void +test_keylisteners (void) +{ + int i; + AccessibleKeystroke stroke; + AccessibleKeystrokeListener *key_listener; + AccessibleKeySet *test_keyset; + + fprintf (stderr, "Testing keyboard listeners ...\n"); + + key_listener = SPI_createAccessibleKeystrokeListener ( + key_listener_cb, &stroke); + + test_keyset = SPI_createAccessibleKeySet (1, "=", NULL, NULL); + + g_assert (SPI_registerAccessibleKeystrokeListener ( + key_listener, + test_keyset, + 0, + SPI_KEY_PRESSED | SPI_KEY_RELEASED, + SPI_KEYLISTENER_CANCONSUME | SPI_KEYLISTENER_ALL_WINDOWS)); + + for (i = 0; i < 3; i++) { + memset (&stroke, 0, sizeof (AccessibleKeystroke)); + g_assert (SPI_generateKeyboardEvent ('=', NULL, SPI_KEY_SYM)); + while (!(key_press_received)) + g_main_context_iteration (NULL, TRUE); + fprintf (stderr, "p [%s]", stroke.keystring); + g_assert (!strcmp (stroke.keystring, "=")); + while (!(key_release_received)) + g_main_context_iteration (NULL, TRUE); + fprintf (stderr, "r [%s]", stroke.keystring); + key_press_received = FALSE; + key_release_received = FALSE; + } + g_assert (SPI_deregisterAccessibleKeystrokeListener (key_listener, 0)); + SPI_freeAccessibleKeySet (test_keyset); + + fprintf (stderr, "\n"); + + AccessibleKeystrokeListener_unref (key_listener); + + g_assert (SPI_generateMouseEvent (100, 100, "rel")); + g_assert (SPI_generateMouseEvent (-50, -50, "rel")); + g_assert (SPI_generateMouseEvent (-50, -50, "rel")); + g_assert (SPI_generateMouseEvent (-1, -1, "b1c")); +} + +int +main (int argc, char **argv) +{ + int leaked, i; + TestWindow *win; + const char *modules; + AccessibleEventListener *global_listener; + + modules = g_getenv ("GTK_MODULES"); + if (!modules || modules [0] == '\0') + putenv ("GTK_MODULES=gail:atk-bridge"); + modules = NULL; + + for (i = 1; i < argc; i++) { + if (!g_strcasecmp (argv [i], "--poke")) + do_poke = TRUE; + } + + gtk_init (&argc, &argv); + + g_assert (!SPI_init ()); + g_assert (SPI_init ()); + g_assert (SPI_getDesktopCount () == 1); + + test_roles (); + test_misc (); + test_desktop (); + test_keylisteners (); + + win = create_test_window (); + + global_listener = SPI_createAccessibleEventListener (global_listener_cb, win); + + g_assert (SPI_registerGlobalEventListener (global_listener, "focus:")); + + fprintf (stderr, "Waiting for focus event ...\n"); + gtk_main (); + + g_assert (SPI_deregisterGlobalEventListenerAll (global_listener)); + AccessibleEventListener_unref (global_listener); + + test_window_destroy (win); + + /* Wait for any pending events from the registry */ + g_usleep (500*1000); + for (i = 0; i < 100; i++) + dbus_connection_read_write_dispatch (SPI_bus(), 5); + + if ((leaked = SPI_exit ())) + g_error ("Leaked %d SPI handles", leaked); + + g_assert (!SPI_exit ()); + + fprintf (stderr, "All tests passed\n"); + + if (g_getenv ("_MEMPROF_SOCKET")) { + fprintf (stderr, "Waiting for memprof\n"); + gtk_main (); + } + + putenv ("AT_BRIDGE_SHUTDOWN=1"); + + return 0; +} |