summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Udaltsov <svu@gnome.org>2003-09-19 23:36:42 +0000
committerSergey Udaltsov <svu@gnome.org>2003-09-19 23:36:42 +0000
commit25fffbb68d26671e5e7dbd9d7f4d57f4a4ca3999 (patch)
treef8b52883028626658a13809c249c307f12c48fe1
downloadlibxklavier-25fffbb68d26671e5e7dbd9d7f4d57f4a4ca3999.tar.gz
Initial revision
-rw-r--r--.cvsignore27
-rw-r--r--AUTHORS1
-rw-r--r--COPYING.LIB481
-rw-r--r--CREDITS2
-rw-r--r--Doxyfile.in808
-rw-r--r--Makefile.am17
-rw-r--r--NEWS1
-rw-r--r--README3
-rw-r--r--RELEASE_NOTES_0_325
-rw-r--r--RELEASE_NOTES_0_411
-rw-r--r--RELEASE_NOTES_0_57
-rw-r--r--RELEASE_NOTES_0_67
-rw-r--r--RELEASE_NOTES_0_73
-rw-r--r--RELEASE_NOTES_0_81
-rw-r--r--RELEASE_NOTES_0_901
-rwxr-xr-xautogen.sh148
-rw-r--r--configure.in89
-rw-r--r--doc/.cvsignore1
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/html/.cvsignore1
-rw-r--r--doc/html/Makefile.am58
-rw-r--r--libxklavier.pc.in11
-rw-r--r--libxklavier.spec.in70
-rw-r--r--libxklavier/.cvsignore1
-rw-r--r--libxklavier/Makefile.am13
-rw-r--r--libxklavier/xklavier.c859
-rw-r--r--libxklavier/xklavier.h469
-rw-r--r--libxklavier/xklavier_config.c708
-rw-r--r--libxklavier/xklavier_config.h389
-rw-r--r--libxklavier/xklavier_config_i18n.c237
-rw-r--r--libxklavier/xklavier_dump.c276
-rw-r--r--libxklavier/xklavier_evt.c473
-rw-r--r--libxklavier/xklavier_private.h139
-rw-r--r--libxklavier/xklavier_props.c359
-rw-r--r--libxklavier/xklavier_util.c441
35 files changed, 6138 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..ad62422
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,27 @@
+ChangeLog
+Doxyfile
+Makefile
+Makefile.in
+aclocal.m4
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+libtool
+libxklavier.prj
+libxklavier.pws
+libxklavier.spec
+ltmain.sh
+stamp-h
+stamp-h.in
+autom4te-2.53.cache
+stamp-h1
+libxklavier.pc
+autom4te.cache
+.tm_project.cache
+stamp-h2
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9d9c9b4
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Sergey V. Udaltsov (svu@users.sourceforge.net)
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..92b8903
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,481 @@
+ 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
+ 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!
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..db35e88
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,2 @@
+Ivan Pascal <pascal@info.tsu.ru>
+Andriy Rysin <arysin@myrealbox.com>
diff --git a/Doxyfile.in b/Doxyfile.in
new file mode 100644
index 0000000..ad44261
--- /dev/null
+++ b/Doxyfile.in
@@ -0,0 +1,808 @@
+# Doxyfile 1.2.8.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = @PACKAGE@
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French,
+# German, Hungarian, Italian, Japanese, Korean, Norwegian, Polish,
+# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a class diagram (in Html and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off.
+
+CLASS_DIAGRAMS = YES
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT =
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = libxklavier
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+FILE_PATTERNS = xklavier.h xklavier_config.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse.
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript and frames is required (for instance Netscape 4.0+
+# or Internet explorer 4.0+).
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT =
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME =
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH =
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..2408119
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,17 @@
+SUBDIRS = libxklavier doc
+
+EXTRA_DIST = libxklavier.spec libxklavier.spec.in \
+ Doxyfile Doxyfile.in autogen.sh \
+ CREDITS libxklavier.pc.in
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libxklavier.pc
+
+doxydocs: libxklavier/xklavier.h libxklavier/xklavier_config.h
+ if test "x$(DO_DOXYGEN)" = "xyes" ; then \
+ doxygen Doxyfile; \
+ fi;
+
+all: doxydocs
+
+.PHONY: doxydocs
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..6f772ef
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+Fixes, code reogranization
diff --git a/README b/README
new file mode 100644
index 0000000..e2e64ff
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+libxklavier - utility library to make XKB stuff easier
+
+Sergey V. Udaltsov
diff --git a/RELEASE_NOTES_0_3 b/RELEASE_NOTES_0_3
new file mode 100644
index 0000000..046dd61
--- /dev/null
+++ b/RELEASE_NOTES_0_3
@@ -0,0 +1,25 @@
+The third development release of libxklavier XKB utility library.
+
+Now libxklavier is in a nearly "feature freeze"/"API freeze" state till 1.0
+release.
+
+Since the only using it application (gswitchit toolkit) is going to have
+massive architectural changes associated with porting to GNOME 2 platform, no
+major changes are planned for the nearest future. Sure, if anyone would be
+interested in adding something useful...
+
+This version adds some extra configuration handling sugar. Now an application
+can backup and restore XKB configuration using X root window's properties in a
+generic way. The standard way X Window is doing this (in "_XKB_RULES_NAMES"
+property only) is not generic enough. Now an application can be sure that the
+property "_XKB_RULES_NAMES_BACKUP" contains the original XKB configuration,
+straight from XF86Config (taking that XklBackupNamesProp is called properly).
+Also, any arbitrary property can be used to keep XKB configuration - if
+necessary.
+
+About the deadlines. I think 1.0 is not going to be released before the
+XFree86 version which would incorporate multiple layouts, XML-based
+configuration registry (now in xfree86_xkb_xml). Only after that great event,
+some testing and user feedback, the world will hopefully see libxklavier 1.0.
+
+Some fixes for the latest gcc/autoconf/automake were done too.
diff --git a/RELEASE_NOTES_0_4 b/RELEASE_NOTES_0_4
new file mode 100644
index 0000000..4f370f1
--- /dev/null
+++ b/RELEASE_NOTES_0_4
@@ -0,0 +1,11 @@
+The forth development release of libxklavier XKB utility library.
+
+Libxklavier is in a nearly "feature freeze"/"API freeze" state till 1.0
+release (not earlier than XFree86 4.3.0 release).
+
+This maintenance release fixes one major bag (which could cause some crashes)
+and one possible memory leak. Also, some code/headers reorganization was
+done - all the header files include all the necessary system headers
+(X11/X???.h) themselves.
+
+Users of libxklavier 0.3 are strongly advised to upgrade.
diff --git a/RELEASE_NOTES_0_5 b/RELEASE_NOTES_0_5
new file mode 100644
index 0000000..f9dbea0
--- /dev/null
+++ b/RELEASE_NOTES_0_5
@@ -0,0 +1,7 @@
+The maintenance/bugfix release of libxklavier XKB utility library.
+
+- Some minor API/ABI changes (sorry for new .so version). In particular, ability to use custom logging callback.
+
+- Usage of pkgconfig (libxklavier.pc created)
+
+- Little fixes and polishing (related to GSwitchIt applet reincarnation as a shared library)
diff --git a/RELEASE_NOTES_0_6 b/RELEASE_NOTES_0_6
new file mode 100644
index 0000000..1caa0ba
--- /dev/null
+++ b/RELEASE_NOTES_0_6
@@ -0,0 +1,7 @@
+Some big big changes in API:
+
+- Handling of secondary groups
+
+- Support for XKB keyboard description patching
+
+- Fixes in root window's _XKB_RF_NAMES_PROP_ATOM property handling
diff --git a/RELEASE_NOTES_0_7 b/RELEASE_NOTES_0_7
new file mode 100644
index 0000000..95f7f45
--- /dev/null
+++ b/RELEASE_NOTES_0_7
@@ -0,0 +1,3 @@
+Major change: support for XFree 4.3.0 and multiple layouts
+
+Minor changes: API improvements, bug fixes
diff --git a/RELEASE_NOTES_0_8 b/RELEASE_NOTES_0_8
new file mode 100644
index 0000000..6c9b0c8
--- /dev/null
+++ b/RELEASE_NOTES_0_8
@@ -0,0 +1 @@
+Minor changes: secondary layout bugfixes, more versatile error processing in configuration activation
diff --git a/RELEASE_NOTES_0_90 b/RELEASE_NOTES_0_90
new file mode 100644
index 0000000..0ff523e
--- /dev/null
+++ b/RELEASE_NOTES_0_90
@@ -0,0 +1 @@
+This release is devoted to bugfixing. Some memory problems, fixed compatibility with setxkmap, a bit of optimization (in atom handling)
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..8ab9db1
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,148 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+PKG_NAME="libxklavier"
+
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`autoconf' installed to."
+ echo "Download the appropriate package for your distribution,"
+ echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+ DIE=1
+}
+
+(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && {
+ (libtool --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`libtool' installed."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+ }
+}
+
+grep "^AM_GNU_GETTEXT" $srcdir/configure.in >/dev/null && {
+ grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \
+ (gettext --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`gettext' installed."
+ echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+ }
+}
+
+grep "^AM_GNOME_GETTEXT" $srcdir/configure.in >/dev/null && {
+ grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \
+ (gettext --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`gettext' installed."
+ echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+ }
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: You must have \`automake' installed."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+ NO_AUTOMAKE=yes
+}
+
+
+# if no automake, don't bother testing for aclocal
+test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "**Error**: Missing \`aclocal'. The version of \`automake'"
+ echo "installed doesn't appear recent enough."
+ echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+ echo "(or a newer version if it is available)"
+ DIE=1
+}
+
+if test "$DIE" -eq 1; then
+ exit 1
+fi
+
+if test -z "$*"; then
+ echo "**Warning**: I am going to run \`configure' with no arguments."
+ echo "If you wish to pass any to it, please specify them on the"
+ echo \`$0\'" command line."
+ echo
+fi
+
+case $CC in
+xlc )
+ am_opt=--include-deps;;
+esac
+
+for coin in `find $srcdir -name configure.in -print`
+do
+ dr=`dirname $coin`
+ if test -f $dr/NO-AUTO-GEN; then
+ echo skipping $dr -- flagged as no auto-gen
+ else
+ echo processing $dr
+ macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin`
+ ( cd $dr
+ aclocalinclude="$ACLOCAL_FLAGS"
+ for k in $macrodirs; do
+ if test -d $k; then
+ aclocalinclude="$aclocalinclude -I $k"
+ ##else
+ ## echo "**Warning**: No such directory \`$k'. Ignored."
+ fi
+ done
+ if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then
+ if grep "sed.*POTFILES" configure.in >/dev/null; then
+ : do nothing -- we still have an old unmodified configure.in
+ else
+ echo "Creating $dr/aclocal.m4 ..."
+ test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+ echo "Running gettextize... Ignore non-fatal messages."
+ echo "no" | gettextize --force --copy
+ echo "Making $dr/aclocal.m4 writable ..."
+ test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+ fi
+ fi
+ if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then
+ echo "Creating $dr/aclocal.m4 ..."
+ test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+ echo "Running gettextize... Ignore non-fatal messages."
+ echo "no" | gettextize --force --copy
+ echo "Making $dr/aclocal.m4 writable ..."
+ test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+ fi
+ if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
+ echo "Running libtoolize..."
+ libtoolize --force --copy
+ fi
+ echo "Running aclocal $aclocalinclude ..."
+ aclocal $aclocalinclude
+ if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
+ echo "Running autoheader..."
+ autoheader
+ fi
+ echo "Running automake --gnu $am_opt ..."
+ automake --add-missing --gnu $am_opt
+ echo "Running autoconf ..."
+ autoconf
+ )
+ fi
+done
+
+#conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
+
+if test x$NOCONFIGURE = x; then
+ echo Running $srcdir/configure $conf_flags "$@" ...
+ $srcdir/configure $conf_flags "$@" \
+ && echo Now type \`make\' to compile $PKG_NAME
+else
+ echo Skipping configure process.
+fi
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..ba63a0c
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,89 @@
+AC_INIT(libxklavier/xklavier.c)
+
+PACKAGE=libxklavier
+MAJOR_VERSION=0
+MINOR_VERSION=95
+VERSION=$MAJOR_VERSION.$MINOR_VERSION
+VERSION_INFO=7:0:0
+
+AC_SUBST(MAJOR_VERSION)
+AC_SUBST(MINOR_VERSION)
+AC_SUBST(PACKAGE)
+AC_SUBST(VERSION)
+AC_SUBST(VERSION_INFO)
+
+AM_CONFIG_HEADER(config.h)
+
+AM_INIT_AUTOMAKE($PACKAGE, $VERSION)
+
+AM_MAINTAINER_MODE
+
+AC_PROG_CC
+AC_ISC_POSIX
+AC_HEADER_STDC
+dnl AC_ARG_PROGRAM
+AM_PROG_LIBTOOL
+
+
+dnl From Bruno Haible.
+dnl From gnoome-vfs
+
+AC_DEFUN([jm_LANGINFO_CODESET],
+[
+ AC_CHECK_HEADERS(langinfo.h)
+ AC_CHECK_FUNCS(nl_langinfo)
+
+ AC_CACHE_CHECK([for nl_langinfo and CODESET], jm_cv_langinfo_codeset,
+ [AC_TRY_LINK([#include <langinfo.h>],
+ [char* cs = nl_langinfo(CODESET);],
+ jm_cv_langinfo_codeset=yes,
+ jm_cv_langinfo_codeset=no)
+ ])
+ if test $jm_cv_langinfo_codeset = yes; then
+ AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
+ [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
+ fi
+])
+
+dnl
+
+jm_LANGINFO_CODESET
+AC_CHECK_FUNCS(setlocale)
+AC_PATH_X
+AC_SUBST(x_libraries)
+AC_ARG_WITH( xkb_base,
+ [ --with-xkb-base=DIR XKB base path (by default it is /usr/X11R6/lib/X11/xkb)],
+ xkb_base="$withval",
+ xkb_base="/usr/X11R6/lib/X11/xkb" )
+
+if ! test -d $xkb_base; then
+ AC_MSG_ERROR([The path $xkb_base does not denote the directory])
+fi
+
+AC_DEFINE_UNQUOTED(XKB_BASE,"${xkb_base}",Base for XKB configuration)
+
+AC_ARG_ENABLE(doxygen,
+[ --disable-doxygen Do not build doxygen documentation],
+, enable_doxygen=yes)
+
+AC_SUBST(DO_DOXYGEN,"${enable_doxygen}")
+
+dnl Checks for libraries.
+PKG_CHECK_MODULES(XML, \
+ libxml-2.0 >= 2.0.0)
+AC_SUBST(XML_LIBS)
+AC_SUBST(XML_CFLAGS)
+
+AC_SUBST(CFLAGS)
+AC_SUBST(LDFLAGS)
+
+AC_OUTPUT([
+Makefile
+libxklavier/Makefile
+libxklavier.spec
+Doxyfile
+doc/Makefile
+doc/html/Makefile
+libxklavier.pc
+])
+
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644
index 0000000..6179e0d
--- /dev/null
+++ b/doc/.cvsignore
@@ -0,0 +1 @@
+Makefile Makefile.in
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..38d9a77
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=html
diff --git a/doc/html/.cvsignore b/doc/html/.cvsignore
new file mode 100644
index 0000000..4414e3f
--- /dev/null
+++ b/doc/html/.cvsignore
@@ -0,0 +1 @@
+Makefile Makefile.in *.html *.css *.gif *.png
diff --git a/doc/html/Makefile.am b/doc/html/Makefile.am
new file mode 100644
index 0000000..dd145d2
--- /dev/null
+++ b/doc/html/Makefile.am
@@ -0,0 +1,58 @@
+EXTRA_WEB_FILES = doxygen.css\
+ doxygen.png
+
+HTML_FILES = annotated.html\
+ files.html\
+ functions.html\
+ globals.html\
+ group__activation.html\
+ group__callbacks.html\
+ group__currents.html\
+ group__debugerr.html\
+ group__enum.html\
+ group__lookup.html\
+ group__props.html\
+ group__settings.html\
+ group__wininfo.html\
+ group__xkbevents.html\
+ group__xkbgroup.html\
+ group__xkbinfo.html\
+ group__xklconfig.html\
+ group__xklconfiginitterm.html\
+ group__xklinitterm.html\
+ index.html\
+ modules.html\
+ struct__XklConfigItem.html\
+ struct__XklConfigItem-members.html\
+ struct__XklConfigRec.html\
+ struct__XklConfigRec-members.html\
+ structXklState.html\
+ structXklState-members.html\
+ xklavier_8h.html\
+ xklavier_8h-source.html\
+ xklavier__config_8h.html\
+ xklavier__config_8h-source.html
+
+DOXYGEN_OUT_FILES = $(EXTRA_WEB_FILES) $(HTML_FILES)
+
+html_to = $(datadir)/doc/$(PACKAGE)-$(VERSION)/html
+
+html_files = $(DOXYGEN_OUT_FILES)
+
+install:
+ @$(NORMAL_INSTALL)
+ if test "x$(DO_DOXYGEN)" = "xyes"; then \
+ $(mkinstalldirs) $(DESTDIR)$(html_to); \
+ for p in $(html_files); do \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(html_to)/$$p; \
+ done; \
+ fi
+
+uninstall:
+ @$(NORMAL_UNINSTALL)
+ for p in $(html_files); do \
+ rm -f $(DESTDIR)$(html_to)/$$p; \
+ done
+
+MAINTAINERCLEANFILES = $(DOXYGEN_OUT_FILES)
+
diff --git a/libxklavier.pc.in b/libxklavier.pc.in
new file mode 100644
index 0000000..1f9badb
--- /dev/null
+++ b/libxklavier.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libxklavier
+Description: libxklavier library
+Requires: libxml-2.0
+Version: @VERSION@
+Libs: -L${libdir} -lxklavier
+Cflags: -I${includedir}/libxklavier
diff --git a/libxklavier.spec.in b/libxklavier.spec.in
new file mode 100644
index 0000000..4825858
--- /dev/null
+++ b/libxklavier.spec.in
@@ -0,0 +1,70 @@
+Name: libxklavier
+Summary: libXklavier library
+Version: @VERSION@
+Release: 1
+License: LGPL
+Group: Development/Libraries
+Url: http://gswitchit.sourceforge.net/
+BuildRequires: doxygen
+Source: http://gswitchit.sourceforge.net/%{name}-%{version}.tar.gz
+Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+
+%description
+This library allows you simplify XKB-related development.
+
+%package devel
+Summary: Libraries, includes, etc to develop libxklavier applications
+Group: Development/Libraries
+Requires: %{name} = %{version}
+
+%description devel
+Libraries, include files, etc you can use to develop libxklavier applications.
+
+%prep
+%setup -q
+
+%build
+
+CONFIG_FLAGS="--prefix=%{_prefix} --libdir=%{_libdir} \
+ --includedir=%{_includedir} --bindir=%{_bindir} \
+ --enable-doxygen"
+
+if [ ! -f configure ]; then
+ CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh $CONFIG_FLAGS
+fi
+
+CFLAGS="$RPM_OPT_FLAGS" ./configure $CONFIG_FLAGS
+
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+DESTDIR="$RPM_BUILD_ROOT" make install
+
+strip $RPM_BUILD_ROOT%{_libdir}/*.so*
+strip $RPM_BUILD_ROOT%{_libdir}/*.a
+
+%clean
+rm -rf %{buildroot}
+rm -rf $RPM_BUILD_DIR/%{name}-%{version}
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+
+%files
+%defattr(-, root, root)
+
+%doc AUTHORS ChangeLog NEWS README COPYING.LIB
+%{_libdir}/lib*.so*
+
+%files devel
+%defattr(-, root, root)
+
+%doc doc/html/*.html doc/html/*.png doc/html/*.css
+%{_libdir}/pkgconfig/*.pc
+%{_libdir}/*a
+%{_includedir}/*
diff --git a/libxklavier/.cvsignore b/libxklavier/.cvsignore
new file mode 100644
index 0000000..4e38b02
--- /dev/null
+++ b/libxklavier/.cvsignore
@@ -0,0 +1 @@
+Makefile Makefile.in *.o *.lo .libs .deps *.la
diff --git a/libxklavier/Makefile.am b/libxklavier/Makefile.am
new file mode 100644
index 0000000..f905e1a
--- /dev/null
+++ b/libxklavier/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = -I. -I$(includedir) $(XML_CFLAGS)
+
+lib_LTLIBRARIES = libxklavier.la
+noinst_HEADERS = xklavier_private.h
+
+xklavierincdir = $(includedir)/libxklavier
+xklavierinc_HEADERS = xklavier.h xklavier_config.h
+
+libxklavier_la_SOURCES = xklavier.c xklavier_evt.c xklavier_util.c xklavier_config.c xklavier_config_i18n.c xklavier_props.c xklavier_dump.c \
+ $(noinst_HEADERS) $(xklavierinc_HEADERS)
+libxklavier_la_LDFLAGS = -version-info @VERSION_INFO@ $(XML_LIBS) -lxkbfile -L$(x_libraries)
+
+
diff --git a/libxklavier/xklavier.c b/libxklavier/xklavier.c
new file mode 100644
index 0000000..de0703d
--- /dev/null
+++ b/libxklavier/xklavier.c
@@ -0,0 +1,859 @@
+#include <stdio.h>
+#include <time.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xlibint.h>
+
+#include "xklavier_private.h"
+
+Display *_xklDpy;
+
+Bool _xklXkbExtPresent;
+
+XkbDescPtr _xklXkb;
+
+XklState _xklCurState;
+
+Window _xklCurClient;
+
+Status _xklLastErrorCode;
+
+const char *_xklLastErrorMsg;
+
+XErrorHandler _xklDefaultErrHandler;
+
+char *_xklIndicatorNames[XkbNumIndicators];
+
+Atom _xklIndicatorAtoms[XkbNumIndicators];
+
+unsigned _xklPhysIndicatorsMask;
+
+int _xklXkbEventType, _xklXkbError;
+
+Atom _xklAtoms[TOTAL_ATOMS];
+
+Window _xklRootWindow;
+
+Bool _xklAllowSecondaryGroupOnce;
+
+int _xklDefaultGroup;
+
+Bool _xklSkipOneRestore;
+
+int _xklSecondaryGroupsMask;
+
+int _xklDebugLevel = 160;
+
+Window _xklPrevAppWindow;
+
+static XklConfigCallback configCallback = NULL;
+static void *configCallbackData;
+
+static XklStateCallback stateCallback = NULL;
+static void *stateCallbackData;
+
+static XklWinCallback winCallback = NULL;
+static void *winCallbackData;
+
+static XklLogAppender logAppender = XklDefaultLogAppender;
+
+static char *groupNames[XkbNumKbdGroups];
+
+static Bool groupPerApp = True;
+
+static Bool handleIndicators = False;
+
+void XklSetIndicatorsHandling( Bool whetherHandle )
+{
+ handleIndicators = whetherHandle;
+}
+
+Bool XklGetIndicatorsHandling( void )
+{
+ return handleIndicators;
+}
+
+const char **XklGetGroupNames( void )
+{
+ return ( const char ** ) groupNames;
+}
+
+void XklSetDebugLevel( int level )
+{
+ _xklDebugLevel = level;
+}
+
+void XklSetGroupPerApp( Bool isSet )
+{
+ groupPerApp = isSet;
+}
+
+Bool XklIsGroupPerApp( void )
+{
+ return groupPerApp;
+}
+
+void XklAllowOneSwitchToSecondaryGroup( void )
+{
+ XklDebug( 150, "Setting allowOneSwitchToSecondaryGroup flag\n" );
+ _xklAllowSecondaryGroupOnce = True;
+}
+
+void XklSetDefaultGroup( int group )
+{
+ _xklDefaultGroup = group;
+}
+
+int XklGetDefaultGroup( void )
+{
+ return _xklDefaultGroup;
+}
+
+void XklSetSecondaryGroupsMask( int mask )
+{
+ _xklSecondaryGroupsMask = mask;
+}
+
+int XklGetSecondaryGroupsMask( void )
+{
+ return _xklSecondaryGroupsMask;
+}
+
+int XklRegisterConfigCallback( XklConfigCallback fun, void *data )
+{
+ configCallback = fun;
+ configCallbackData = data;
+ return 0;
+}
+
+int XklRegisterStateCallback( XklStateCallback fun, void *data )
+{
+ stateCallback = fun;
+ stateCallbackData = data;
+ return 0;
+}
+
+int XklRegisterWindowCallback( XklWinCallback fun, void *data )
+{
+ winCallback = fun;
+ winCallbackData = data;
+ return 0;
+}
+
+void XklSetLogAppender( XklLogAppender fun )
+{
+ logAppender = fun;
+}
+
+int XklInit( Display * a_dpy )
+{
+ int opcode;
+ int scr;
+
+ _xklDefaultErrHandler =
+ XSetErrorHandler( ( XErrorHandler ) _XklErrHandler );
+
+ /* Lets begin */
+ _xklXkbExtPresent = XkbQueryExtension( _xklDpy = a_dpy,
+ &opcode, &_xklXkbEventType,
+ &_xklXkbError, NULL, NULL );
+ if( !_xklXkbExtPresent )
+ {
+ return -1;
+ }
+
+ scr = DefaultScreen( _xklDpy );
+ _xklRootWindow = RootWindow( _xklDpy, scr );
+ XklDebug( 160,
+ "xkbEvenType: %X, xkbError: %X, display: %p, root: " WINID_FORMAT
+ "\n", _xklXkbEventType, _xklXkbError, _xklDpy, _xklRootWindow );
+
+ _xklAtoms[WM_NAME] = XInternAtom( _xklDpy, "WM_NAME", False );
+ _xklAtoms[WM_STATE] = XInternAtom( _xklDpy, "WM_STATE", False );
+ _xklAtoms[XKLAVIER_STATE] = XInternAtom( _xklDpy, "XKLAVIER_STATE", False );
+ _xklAtoms[XKLAVIER_TRANSPARENT] =
+ XInternAtom( _xklDpy, "XKLAVIER_TRANSPARENT", False );
+ _xklAtoms[XKB_RF_NAMES_PROP_ATOM] =
+ XInternAtom( _xklDpy, _XKB_RF_NAMES_PROP_ATOM, False );
+ _xklAtoms[XKB_RF_NAMES_PROP_ATOM_BACKUP] =
+ XInternAtom( _xklDpy, "_XKB_RULES_NAMES_BACKUP", False );
+
+ _xklAllowSecondaryGroupOnce = False;
+ _xklSkipOneRestore = False;
+ _xklDefaultGroup = -1;
+ _xklSecondaryGroupsMask = 0L;
+ _xklPrevAppWindow = 0;
+
+ return _XklLoadAllInfo( )? 0 : _xklLastErrorCode;
+}
+
+int XklStartListen( )
+{
+ XklResumeListen( );
+ _XklLoadWindowTree( );
+ XFlush( _xklDpy );
+ return 0;
+}
+
+int XklPauseListen( )
+{
+ XkbSelectEvents( _xklDpy, XkbUseCoreKbd, XkbAllEventsMask, 0 );
+// XkbSelectEventDetails( _xklDpy,
+ // XkbUseCoreKbd,
+ // XkbStateNotify,
+ // 0,
+ // 0 );
+
+ //!!_XklSelectInput( _xklRootWindow, 0 );
+ return 0;
+}
+
+int XklResumeListen( )
+{
+ /* What events we want */
+#define XKB_EVT_MASK \
+ (XkbStateNotifyMask| \
+ XkbNamesNotifyMask| \
+ XkbControlsNotifyMask| \
+ XkbIndicatorStateNotifyMask| \
+ XkbIndicatorMapNotifyMask| \
+ XkbNewKeyboardNotifyMask)
+
+ XkbSelectEvents( _xklDpy, XkbUseCoreKbd, XKB_EVT_MASK, XKB_EVT_MASK );
+
+#define XKB_STATE_EVT_DTL_MASK \
+ (XkbGroupStateMask)
+
+ XkbSelectEventDetails( _xklDpy,
+ XkbUseCoreKbd,
+ XkbStateNotify,
+ XKB_STATE_EVT_DTL_MASK, XKB_STATE_EVT_DTL_MASK );
+
+#define XKB_NAMES_EVT_DTL_MASK \
+ (XkbGroupNamesMask|XkbIndicatorNamesMask)
+
+ XkbSelectEventDetails( _xklDpy,
+ XkbUseCoreKbd,
+ XkbNamesNotify,
+ XKB_NAMES_EVT_DTL_MASK, XKB_NAMES_EVT_DTL_MASK );
+
+ _XklSelectInputMerging( _xklRootWindow,
+ SubstructureNotifyMask | PropertyChangeMask );
+ _XklGetRealState( &_xklCurState );
+ return 0;
+}
+
+int XklStopListen( )
+{
+ XklPauseListen( );
+ return 0;
+}
+
+int XklTerm( )
+{
+ XSetErrorHandler( ( XErrorHandler ) _xklDefaultErrHandler );
+ configCallback = NULL;
+ stateCallback = NULL;
+ winCallback = NULL;
+
+ logAppender = XklDefaultLogAppender;
+ _XklFreeAllInfo( );
+
+ return 0;
+}
+
+Bool XklGrabKey( int key, unsigned modifiers )
+{
+ int keyCode;
+ Bool retCode;
+ char *keyName;
+
+ keyCode = XKeysymToKeycode( _xklDpy, key );
+ keyName = XKeysymToString( key );
+
+ XklDebug( 100, "listen to the key %X(%d/%s)/%d\n", key, keyCode, keyName,
+ modifiers );
+
+ if( ( KeyCode ) NULL == keyCode )
+ return False;
+
+ _xklLastErrorCode = Success;
+
+ retCode = XGrabKey( _xklDpy, keyCode, modifiers, _xklRootWindow,
+ True, GrabModeAsync, GrabModeAsync );
+ XSync( _xklDpy, False );
+
+ XklDebug( 100, "trying to listen: %d/%d\n", retCode, _xklLastErrorCode );
+
+ retCode = ( _xklLastErrorCode == Success );
+
+ if( !retCode )
+ _xklLastErrorMsg = "Could not grab the key";
+
+ return retCode;
+}
+
+Bool XklUngrabKey( int key, unsigned modifiers )
+{
+ int keyCode;
+
+ keyCode = XKeysymToKeycode( _xklDpy, key );
+
+ if( ( KeyCode ) NULL == keyCode )
+ return False;
+
+ return Success == XUngrabKey( _xklDpy, keyCode, 0, _xklRootWindow );
+}
+
+unsigned XklGetNumGroups( )
+{
+ return _xklXkb->ctrls->num_groups;
+}
+
+int XklGetNextGroup( )
+{
+ return ( _xklCurState.group + 1 ) % _xklXkb->ctrls->num_groups;
+}
+
+int XklGetPrevGroup( )
+{
+ int n = _xklXkb->ctrls->num_groups;
+ return ( _xklCurState.group + n - 1 ) % n;
+}
+
+int XklGetRestoreGroup( )
+{
+ XklState state;
+ if( _xklCurClient == ( Window ) NULL )
+ {
+ XklDebug( 150, "cannot restore without current client\n" );
+ } else if( XklGetState( _xklCurClient, &state ) )
+ {
+ return state.group;
+ } else
+ XklDebug( 150,
+ "Unbelievable: current client " WINID_FORMAT
+ ", '%s' has no group\n", _xklCurClient,
+ _XklGetDebugWindowTitle( _xklCurClient ) );
+ return 0;
+}
+
+void XklSetTransparent( Window win, Bool transparent )
+{
+ Window appWin;
+ Bool wasTransparent;
+ XklDebug( 150, "setting transparent flag %d for " WINID_FORMAT "\n",
+ transparent, win );
+
+ if( !_XklGetAppWindow( win, &appWin ) )
+ {
+ XklDebug( 150, "No app window!\n" );
+ appWin = win;
+// return;
+ }
+
+ wasTransparent = XklIsTransparent( appWin );
+ XklDebug( 150, "appwin " WINID_FORMAT " was %stransparent\n", appWin,
+ wasTransparent ? "" : "not " );
+ if( transparent && !wasTransparent )
+ {
+ CARD32 prop = 1;
+ XChangeProperty( _xklDpy, appWin, _xklAtoms[XKLAVIER_TRANSPARENT],
+ XA_INTEGER, 32, PropModeReplace,
+ ( const unsigned char * ) &prop, 1 );
+ } else if( !transparent && wasTransparent )
+ {
+ XDeleteProperty( _xklDpy, appWin, _xklAtoms[XKLAVIER_TRANSPARENT] );
+ }
+}
+
+Bool XklIsTransparent( Window win )
+{
+ Window appWin;
+
+ if( !_XklGetAppWindow( win, &appWin ) )
+ return False;
+ return _XklIsTransparentAppWindow( appWin );
+
+}
+
+/**
+ * "Adds" app window to the set of managed windows.
+ * Actually, no data structures involved. The only thing we do is save app state
+ * and register ourselves us listeners.
+ * Note: User's callback is called
+ */
+void _XklAddAppWindow( Window appWin, Window parent, Bool ignoreExistingState,
+ XklState * initState )
+{
+ XklState state = *initState;
+ int defGroupToUse = -1;
+
+ if( appWin == _xklRootWindow )
+ XklDebug( 150, "??? root app win ???\n" );
+
+ XklDebug( 150, "Trying to add window " WINID_FORMAT "/%s with group %d\n",
+ appWin, _XklGetDebugWindowTitle( appWin ), initState->group );
+
+ if( !ignoreExistingState )
+ {
+ Bool have_state = _XklGetAppState( appWin, &state );
+
+ if( have_state )
+ {
+ XklDebug( 150,
+ "The window " WINID_FORMAT
+ " does not require to be added, it already has the xklavier state \n",
+ appWin );
+ return;
+ }
+ }
+
+ if( winCallback != NULL )
+ defGroupToUse = ( *winCallback ) ( appWin, parent, winCallbackData );
+
+ if( defGroupToUse == -1 )
+ defGroupToUse = _xklDefaultGroup;
+
+ if( defGroupToUse != -1 )
+ state.group = defGroupToUse;
+
+ _XklSaveAppState( appWin, &state );
+ _XklSelectInputMerging( appWin, FocusChangeMask | PropertyChangeMask );
+
+ if( defGroupToUse != -1 )
+ {
+ if( _xklCurClient == appWin )
+ {
+ if( ( _xklSecondaryGroupsMask & ( 1 << defGroupToUse ) ) != 0 )
+ XklAllowOneSwitchToSecondaryGroup( );
+ XklLockGroup( defGroupToUse );
+ }
+ }
+
+ if( parent == ( Window ) NULL )
+ parent = _XklGetRegisteredParent( appWin );
+
+ XklDebug( 150, "done\n" );
+}
+
+/**
+ * Checks the window and goes up
+ */
+Bool _XklGetAppWindowBottomToTop( Window win, Window * appWin_return )
+{
+ Window parent = ( Window ) NULL, rwin = ( Window ) NULL, *children = NULL;
+ int num = 0;
+
+ if( win == ( Window ) NULL || win == _xklRootWindow )
+ {
+ *appWin_return = win;
+ _xklLastErrorMsg = "The window is either 0 or root";
+ return False;
+ }
+
+ if( _XklHasWmState( win ) )
+ {
+ *appWin_return = win;
+ return True;
+ }
+
+ _xklLastErrorCode =
+ _XklStatusQueryTree( _xklDpy, win, &rwin, &parent, &children, &num );
+
+ if( _xklLastErrorCode != Success )
+ {
+ *appWin_return = ( Window ) NULL;
+ return False;
+ }
+
+ if( children != NULL )
+ XFree( children );
+
+ return _XklGetAppWindowBottomToTop( parent, appWin_return );
+}
+
+/**
+ * Recursively finds "App window" (window with WM_STATE) for given window.
+ * First, checks the window itself
+ * Then, for first level of recursion, checks childen,
+ * Then, goes to parent.
+ * NOTE: root window cannot be "App window" under normal circumstances
+ */
+Bool _XklGetAppWindow( Window win, Window * appWin_return )
+{
+ Window parent = ( Window ) NULL,
+ rwin = ( Window ) NULL, *children = NULL, *child;
+ int num = 0;
+ Bool rv;
+
+ if( win == ( Window ) NULL || win == _xklRootWindow )
+ {
+ *appWin_return = ( Window ) NULL;
+ _xklLastErrorMsg = "The window is either 0 or root";
+ XklDebug( 150,
+ "Window " WINID_FORMAT
+ " is either 0 or root so could not get the app window for it\n",
+ win );
+ return False;
+ }
+
+ if( _XklHasWmState( win ) )
+ {
+ *appWin_return = win;
+ return True;
+ }
+
+ _xklLastErrorCode =
+ _XklStatusQueryTree( _xklDpy, win, &rwin, &parent, &children, &num );
+
+ if( _xklLastErrorCode != Success )
+ {
+ *appWin_return = ( Window ) NULL;
+ XklDebug( 150,
+ "Could not get tree for window " WINID_FORMAT
+ " so could not get the app window for it\n", win );
+ return False;
+ }
+
+ /**
+ * Here we first check the children (in case win is just above some "App Window")
+ * and then go upstairs
+ */
+ child = children;
+ while( num )
+ {
+ if( _XklHasWmState( *child ) )
+ {
+ *appWin_return = *child;
+ if( children != NULL )
+ XFree( children );
+ return True;
+ }
+ child++;
+ num--;
+ }
+
+ if( children != NULL )
+ XFree( children );
+
+ rv = _XklGetAppWindowBottomToTop( parent, appWin_return );
+
+ if( !rv )
+ XklDebug( 200, "Could not get the app window for " WINID_FORMAT "/%s\n",
+ win, _XklGetDebugWindowTitle( win ) );
+
+ return rv;
+}
+
+/**
+ * Loads the tree recursively.
+ */
+Bool _XklLoadWindowTree( )
+{
+ Window focused;
+ int revert;
+ Bool retval, haveAppWindow;
+
+ retval = _XklLoadSubtree( _xklRootWindow, 0, &_xklCurState );
+
+ XGetInputFocus( _xklDpy, &focused, &revert );
+
+ XklDebug( 160, "initially focused: " WINID_FORMAT ", '%s'\n", focused );
+
+ haveAppWindow = _XklGetAppWindow( focused, &_xklCurClient );
+
+ if( haveAppWindow )
+ {
+ Bool haveState = _XklGetAppState( _xklCurClient, &_xklCurState );
+ XklDebug( 160,
+ "initial _xklCurClient: " WINID_FORMAT
+ ", '%s' %s state %d/%X\n", _xklCurClient,
+ _XklGetDebugWindowTitle( _xklCurClient ),
+ ( haveState ? "with" : "without" ),
+ ( haveState ? _xklCurState.group : -1 ),
+ ( haveState ? _xklCurState.indicators : -1 ) );
+ } else
+ {
+ XklDebug( 160,
+ "could not find initial app. Probably, focus belongs to some WM service window. Will try to survive:)" );
+ }
+
+ return retval;
+}
+
+#define KBD_MASK \
+ ( 0 )
+#define CTRLS_MASK \
+ ( XkbSlowKeysMask )
+#define NAMES_MASK \
+ ( XkbGroupNamesMask | XkbIndicatorNamesMask )
+
+void _XklFreeAllInfo( )
+{
+ if( _xklXkb != NULL )
+ {
+ int i;
+ char **groupName = groupNames;
+ for( i = _xklXkb->ctrls->num_groups; --i >= 0; groupName++ )
+ if( *groupName )
+ XFree( *groupName );
+ XkbFreeKeyboard( _xklXkb, XkbAllComponentsMask, True );
+ _xklXkb = NULL;
+ }
+}
+
+/**
+ * Load some XKB parameters
+ */
+Bool _XklLoadAllInfo( )
+{
+ int i;
+ unsigned bit;
+ Atom *gna;
+ char **groupName;
+
+ _xklXkb = XkbGetMap( _xklDpy, KBD_MASK, XkbUseCoreKbd );
+ if( _xklXkb == NULL )
+ {
+ _xklLastErrorMsg = "Could not load keyboard";
+ return False;
+ }
+
+ _xklLastErrorCode = XkbGetControls( _xklDpy, CTRLS_MASK, _xklXkb );
+
+ if( _xklLastErrorCode != Success )
+ {
+ _xklLastErrorMsg = "Could not load controls";
+ return False;
+ }
+
+ XklDebug( 200, "found %d groups\n", _xklXkb->ctrls->num_groups );
+
+ _xklLastErrorCode = XkbGetNames( _xklDpy, NAMES_MASK, _xklXkb );
+
+ if( _xklLastErrorCode != Success )
+ {
+ _xklLastErrorMsg = "Could not load names";
+ return False;
+ }
+
+ gna = _xklXkb->names->groups;
+ groupName = groupNames;
+ for( i = _xklXkb->ctrls->num_groups; --i >= 0; gna++, groupName++ )
+ {
+ *groupName = XGetAtomName( _xklDpy,
+ *gna == None ?
+ XInternAtom( _xklDpy, "-", False ) : *gna );
+ XklDebug( 200, "group %d has name [%s]\n", i, *groupName );
+ }
+
+ _xklLastErrorCode =
+ XkbGetIndicatorMap( _xklDpy, XkbAllIndicatorsMask, _xklXkb );
+
+ if( _xklLastErrorCode != Success )
+ {
+ _xklLastErrorMsg = "Could not load indicator map";
+ return False;
+ }
+
+ for( i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1 )
+ {
+ Atom a = _xklXkb->names->indicators[i];
+ if( a != None )
+ _xklIndicatorNames[i] = XGetAtomName( _xklDpy, a );
+ else
+ _xklIndicatorNames[i] = "";
+
+ XklDebug( 200, "Indicator[%d] is %s\n", i, _xklIndicatorNames[i] );
+ }
+
+ XklDebug( 200, "Real indicators are %X\n",
+ _xklXkb->indicators->phys_indicators );
+
+ if( configCallback != NULL )
+ ( *configCallback ) ( configCallbackData );
+
+ return True;
+}
+
+void _XklDebug( const char file[], const char function[], int level,
+ const char format[], ... )
+{
+ va_list lst;
+
+ if( level > _xklDebugLevel )
+ return;
+
+ va_start( lst, format );
+ if( logAppender != NULL )
+ ( *logAppender ) ( file, function, level, format, lst );
+ va_end( lst );
+}
+
+void XklDefaultLogAppender( const char file[], const char function[],
+ int level, const char format[], va_list args )
+{
+ time_t now = time( NULL );
+ fprintf( stdout, "[%08ld,%03d,%s:%s/] \t", now, level, file, function );
+ vfprintf( stdout, format, args );
+}
+
+#define PROP_LENGTH 2
+
+/**
+ * Gets the state from the window property
+ */
+Bool _XklGetAppState( Window appWin, XklState * state_return )
+{
+ Atom type_ret;
+ int format_ret;
+ unsigned long nitems, rest;
+ CARD32 *prop = NULL;
+ Bool ret = False;
+
+ int grp = -1;
+ unsigned inds = -1;
+
+ if( ( XGetWindowProperty
+ ( _xklDpy, appWin, _xklAtoms[XKLAVIER_STATE], 0L, PROP_LENGTH, False,
+ XA_INTEGER, &type_ret, &format_ret, &nitems, &rest,
+ ( unsigned char ** ) &prop ) == Success )
+ && ( type_ret == XA_INTEGER ) && ( format_ret == 32 ) )
+ {
+ grp = prop[0];
+ if( grp >= _xklXkb->ctrls->num_groups || grp < 0 )
+ grp = 0;
+
+ inds = prop[1];
+
+ if( state_return != NULL )
+ {
+ state_return->group = grp;
+ state_return->indicators = inds;
+ }
+ if( prop != NULL )
+ XFree( prop );
+
+ ret = True;
+ }
+
+ if( ret )
+ XklDebug( 150,
+ "Appwin " WINID_FORMAT
+ ", '%s' has the group %d, indicators %X\n", appWin,
+ _XklGetDebugWindowTitle( appWin ), grp, inds );
+ else
+ XklDebug( 150, "Appwin " WINID_FORMAT ", '%s' does not have state\n",
+ appWin, _XklGetDebugWindowTitle( appWin ) );
+
+ return ret;
+}
+
+/**
+ * Deletes the state from the window properties
+ */
+void _XklDelAppState( Window appWin )
+{
+ XDeleteProperty( _xklDpy, appWin, _xklAtoms[XKLAVIER_STATE] );
+}
+
+/**
+ * Saves the state into the window properties
+ */
+void _XklSaveAppState( Window appWin, XklState * state )
+{
+ CARD32 prop[PROP_LENGTH];
+
+ prop[0] = state->group;
+ prop[1] = state->indicators;
+
+ XChangeProperty( _xklDpy, appWin, _xklAtoms[XKLAVIER_STATE], XA_INTEGER,
+ 32, PropModeReplace, ( const unsigned char * ) prop,
+ PROP_LENGTH );
+
+ XklDebug( 160,
+ "Saved the group %d, indicators %X for appwin " WINID_FORMAT "\n",
+ state->group, state->indicators, appWin );
+}
+
+/**
+ * Just selects some events from the window.
+ */
+void _XklSelectInput( Window win, long mask )
+{
+ if( _xklRootWindow == win )
+ XklDebug( 160,
+ "Someone is looking for " WINID_FORMAT " on root window ***\n",
+ mask );
+
+ XSelectInput( _xklDpy, win, mask );
+}
+
+void _XklSelectInputMerging( Window win, long mask )
+{
+ XWindowAttributes attrs;
+ long oldmask = 0L, newmask;
+ memset( &attrs, 0, sizeof( attrs ) );
+ if( XGetWindowAttributes( _xklDpy, win, &attrs ) )
+ oldmask = attrs.your_event_mask;
+
+ newmask = oldmask | mask;
+ if( newmask != oldmask )
+ _XklSelectInput( win, newmask );
+}
+
+void _XklTryCallStateCallback( XklStateChange changeType,
+ XklState * oldState )
+{
+ int group = _xklCurState.group;
+ Bool restore = oldState->group == group;
+
+ XklDebug( 150,
+ "changeType: %d, group: %d, secondaryGroupMask: %X, allowsecondary: %d\n",
+ changeType, group, _xklSecondaryGroupsMask,
+ _xklAllowSecondaryGroupOnce );
+
+ if( changeType == GROUP_CHANGED )
+ {
+ if( !restore )
+ {
+ if( ( _xklSecondaryGroupsMask & ( 1 << group ) ) != 0 &&
+ !_xklAllowSecondaryGroupOnce )
+ {
+ XklDebug( 150, "secondary -> go next\n" );
+ group = XklGetNextGroup( );
+ XklLockGroup( group );
+ return; // we do not need to revalidate
+ }
+ }
+ _xklAllowSecondaryGroupOnce = False;
+ }
+ if( stateCallback != NULL )
+ {
+
+ ( *stateCallback ) ( changeType, _xklCurState.group,
+ restore, stateCallbackData );
+ }
+}
+
+Bool _XklIsTransparentAppWindow( Window appWin )
+{
+ Atom type_ret;
+ int format_ret;
+ unsigned long nitems, rest;
+ CARD32 *prop = NULL;
+ Bool retVal;
+ if( ( XGetWindowProperty
+ ( _xklDpy, appWin, _xklAtoms[XKLAVIER_TRANSPARENT], 0L, 1, False,
+ XA_INTEGER, &type_ret, &format_ret, &nitems, &rest,
+ ( unsigned char ** ) &prop ) == Success )
+ && ( type_ret == XA_INTEGER ) && ( format_ret == 32 ) )
+ {
+ if( prop != NULL )
+ XFree( prop );
+ return True;
+ }
+ return False;
+}
diff --git a/libxklavier/xklavier.h b/libxklavier/xklavier.h
new file mode 100644
index 0000000..51fb087
--- /dev/null
+++ b/libxklavier/xklavier.h
@@ -0,0 +1,469 @@
+/**
+ * @file xklavier.h
+ */
+
+#ifndef __XKLAVIER_H__
+#define __XKLAVIER_H__
+
+#include <stdarg.h>
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef enum
+ {
+/**
+ * Group was changed
+ */
+ GROUP_CHANGED,
+/**
+ * Indicators were changed
+ */
+ INDICATORS_CHANGED
+ }
+ XklStateChange;
+
+/**
+ * XKB state. Can be global or per-window
+ */
+ typedef struct
+ {
+/** selected group */
+ int group;
+/** set of active indicators */
+ unsigned indicators;
+ }
+ XklState;
+
+/**
+ * @defgroup xklinitterm Library initialization and termination
+ * @{
+ */
+
+/**
+ * Initializes internal structures. Does not start actual listening though.
+ * Some apps can use Xklavier for information retrieval but not for actual
+ * processing.
+ * @param dpy is an open display, will be tested for XKB extension
+ * @return 0 if OK, otherwise last X error
+ * (special case: -1 if XKB extension is not present)
+ */
+ extern int XklInit( Display * dpy );
+
+/**
+ * Terminates everything...
+ */
+ extern int XklTerm( void );
+
+/** @} */
+
+/**
+ * @defgroup xkbevents XKB event handling and management
+ * @{
+ */
+
+/**
+ * Starts listening for XKB-related events
+ * @return 0
+ */
+ extern int XklStartListen( void );
+
+/**
+ * Stops listening for XKB-related events
+ * @return 0
+ */
+ extern int XklStopListen( void );
+
+/**
+ * Temporary pauses listening for XKB-related events
+ * @return 0
+ */
+ extern int XklPauseListen( void );
+
+/**
+ * Resumes listening for XKB-related events
+ * @return 0
+ */
+ extern int XklResumeListen( void );
+
+/**
+ * Grabs some key
+ * @param key is a keysym
+ * @param modifiers is a bitmask of modifiers
+ * @return True on success
+ */
+ extern Bool XklGrabKey( int key, unsigned modifiers );
+
+/**
+ * Ungrabs some key
+ * @param key is a keysym
+ * @param modifiers is a bitmask of modifiers
+ * @return True on success
+ */
+ extern Bool XklUngrabKey( int key, unsigned modifiers );
+
+/**
+ * Processes X events. Should be included into the main event cycle of an
+ * application. One of the most important functions.
+ * @param evt is delivered X event
+ * @return 0 if the event it processed - 1 otherwise
+ * @see XklStartListen
+ */
+ extern int XklFilterEvents( XEvent * evt );
+
+/**
+ * Allows to switch (once) to the secondary group
+ */
+ extern void XklAllowOneSwitchToSecondaryGroup( void );
+
+/** @} */
+
+/**
+ * @defgroup currents Current state of the library
+ * @{
+ */
+
+/**
+ * @return currently focused window
+ */
+ extern Window XklGetCurrentWindow( void );
+
+/**
+ * @return current state of the keyboard (in XKB terms).
+ * Returned value is a statically allocated buffer, should not be freed.
+ */
+ extern XklState *XklGetCurrentState( void );
+
+/** @} */
+
+/**
+ * @defgroup wininfo Per-window information
+ * @{
+ */
+
+/**
+ * @return the window title of some window or NULL.
+ * If not NULL, it should be freed with XFree
+ */
+ extern char *XklGetWindowTitle( Window w );
+
+/**
+ * Finds the state for a given window (for its "App window").
+ * @param win is a target window
+ * @param state_return is a structure to store the state
+ * @return True on success, otherwise False
+ * (the error message can be obtained using XklGetLastError).
+ */
+ extern Bool XklGetState( Window win, XklState * state_return );
+
+/**
+ * Drops the state of a given window (of its "App window").
+ * @param win is a target window
+ */
+ extern void XklDelState( Window win );
+
+/**
+ * Stores ths state for a given window
+ * @param win is a target window
+ * @param state is a new state of the window
+ */
+ extern void XklSaveState( Window win, XklState * state );
+
+/**
+ * Sets the "transparent" flag. It means focus switching onto
+ * this window will never change the state.
+ * @param win is the window do set the flag for.
+ * @param transparent - if true, the windows is transparent.
+ * @see XklIsTranspatent
+ */
+ extern void XklSetTransparent( Window win, Bool transparent );
+
+/**
+ * Returns "transparent" flag.
+ * @param win is the window to get the transparent flag from.
+ * @see XklSetTranspatent
+ */
+ extern Bool XklIsTransparent( Window win );
+
+/**
+ * Checks whether 2 windows have the same App Window
+ * @param win1 is first window
+ * @param win2 is second window
+ * @return True is windows are in the same application
+ */
+ extern Bool XklIsSameApp( Window win1, Window win2 );
+
+/** @} */
+
+/**
+ * @defgroup xkbinfo Various XKB configuration info
+ * @{
+ */
+
+/**
+ * @return the total number of groups in the current XKB configuration
+ * (keyboard)
+ */
+ extern unsigned XklGetNumGroups( void );
+
+/**
+ * @return the array of group names for the current XKB configuration
+ * (keyboard).
+ * This array is static, should not be freed
+ */
+ extern const char **XklGetGroupNames( void );
+
+/**
+ * @return the array of indicator names for the current XKB configuration
+ * (keyboard).
+ * This array is static, should not be freed
+ */
+ extern const char **XklGetIndicatorNames( void );
+
+/** @} */
+
+/**
+ * @defgroup xkbgroup XKB group calculation and change
+ * @{
+ */
+
+/**
+ * Calculates next group id. Does not change the state of anything.
+ * @return next group id
+ */
+ extern int XklGetNextGroup( void );
+
+/**
+ * Calculates prev group id. Does not change the state of anything.
+ * @return prev group id
+ */
+ extern int XklGetPrevGroup( void );
+
+/**
+ * @return saved group id of the current client.
+ * Does not change the state of anything.
+ */
+ extern int XklGetRestoreGroup( void );
+
+/**
+ * Locks the group. Can be used after XklGetXXXGroup functions
+ * @param group is a group number for locking
+ * @see XklGetNextGroup
+ * @see XklGetPrevGroup
+ * @see XklGetRestoreGroup
+ */
+ extern void XklLockGroup( int group );
+
+/** @} */
+
+/**
+ * @defgroup callbacks Application callbacks support
+ * @{
+ */
+
+/**
+ * Used for notifying application of the XKB configuration change.
+ * @param userData is anything which can be stored into the pointer
+ * @see XklRegisterConfigCallback
+ */
+ typedef void ( *XklConfigCallback ) ( void *userData );
+
+/**
+ * Registers user callback. Only one callback can be registered at a time
+ * @param fun is the function to call
+ * @param userData is the data to pass
+ * @see XklConfigCallback
+ */
+ extern int XklRegisterConfigCallback( XklConfigCallback fun,
+ void *userData );
+
+/**
+ * Used for notifying application of new window creation (actually,
+ * registration).
+ * @param win is a new window
+ * @param parent is a new window's parent
+ * @param userData is anything which can be stored into the pointer
+ * @return the initial group id for the window (-1 to use the default value)
+ * @see XklRegisterConfigCallback
+ * @see XklSetDefaultGroup
+ * @see XklGetDefaultGroup
+ */
+ typedef int ( *XklWinCallback ) ( Window win, Window parent,
+ void *userData );
+
+/**
+ * Registers user callback. Only one callback can be registered at a time
+ * @param fun is the function to call
+ * @param userData is the data to pass
+ * @see XklWindowCallback
+ */
+ extern int XklRegisterWindowCallback( XklWinCallback fun, void *userData );
+
+/**
+ * Used for notifying application of the window state change.
+ * @param changeType is a mask of changes
+ * @param group is a new group
+ * @param restore is indicator of whether this state is restored from
+ * saved state of set as new.
+ * @param userData is anything which can be stored into the pointer
+ * @see XklRegisterConfigCallback
+ */
+ typedef void ( *XklStateCallback ) ( XklStateChange changeType, int group,
+ Bool restore, void *userData );
+
+/**
+ * Registers user callback. Only one callback can be registered at a time
+ * @param fun is the function to call
+ * @param userData is the data to pass
+ * @see XklStateCallback
+ */
+ extern int XklRegisterStateCallback( XklStateCallback fun, void *userData );
+
+/** @} */
+
+/**
+ * @defgroup settings Settings for event processing
+ * @{
+ */
+
+/**
+ * Sets the configuration parameter: group per application
+ * @param isGlobal is a new parameter value
+ */
+ extern void XklSetGroupPerApp( Bool isGlobal );
+
+/**
+ * @return the value of the parameter: group per application
+ */
+ extern Bool XklIsGroupPerApp( void );
+
+/**
+ * Sets the configuration parameter: perform indicators handling
+ * @param whetherHandle is a new parameter value
+ */
+ extern void XklSetIndicatorsHandling( Bool whetherHandle );
+
+/**
+ * @return the value of the parameter: perform indicator handling
+ */
+ extern Bool XklGetIndicatorsHandling( void );
+
+/**
+ * Sets the secondary groups (one bit per group).
+ * Secondary groups require explicit "allowance" for switching
+ * @param mask is a new group mask
+ * @see XklAllowOneSwitchToSecondaryGroup
+ */
+ extern void XklSetSecondaryGroupsMask( int mask );
+
+/**
+ * @return the secondary group mask
+ */
+ extern int XklGetSecondaryGroupsMask( void );
+
+/**
+ * Configures the default group set on window creation.
+ * If -1, no default group is used
+ * @param group the default group
+ */
+ extern void XklSetDefaultGroup( int group );
+
+/**
+ * Returns the default group set on window creation
+ * If -1, no default group is used
+ * @return the default group
+ */
+ extern int XklGetDefaultGroup( void );
+
+/** @} */
+
+/**
+ * @defgroup debugerr Debugging, error processing
+ * @{
+ */
+
+/**
+ * @return the text message (statically allocated) of the last error
+ */
+ extern const char *XklGetLastError( void );
+
+/**
+ * Output (optionally) some debug info
+ * @param file is the name of the source file.
+ * Preprocessor symbol__FILE__ should be used here
+ * @param function is a name of the function
+ * Preprocessor symbol__func__ should be used here
+ * @param level is a level of the message
+ * @param format is a format (like in printf)
+ * @see XklDebug
+ */
+ extern void _XklDebug( const char file[], const char function[], int level,
+ const char format[], ... );
+
+/**
+ * Custom log output method for _XklDebug. This appender is NOT called if the
+ * level of the message is greater than currently set debug level.
+ *
+ * @param file is the name of the source file.
+ * Preprocessor symbol__FILE__ should be used here
+ * @param function is a name of the function
+ * Preprocessor symbol__func__ should be used here
+ * @param level is a level of the message
+ * @param format is a format (like in printf)
+ * @param args is the list of parameters
+ * @see _XklDebug
+ * @see XklSetDebugLevel
+ */
+ typedef void ( *XklLogAppender ) ( const char file[], const char function[],
+ int level, const char format[],
+ va_list args );
+
+/**
+ * Default log output method. Sends everything to stdout.
+ *
+ * @param file is the name of the source file.
+ * Preprocessor symbol__FILE__ should be used here
+ * @param function is a name of the function
+ * Preprocessor symbol__func__ should be used here
+ * @param level is a level of the message
+ * @param format is a format (like in printf)
+ * @param args is the list of parameters
+ */
+ extern void XklDefaultLogAppender( const char file[], const char function[],
+ int level, const char format[],
+ va_list args );
+
+/**
+ * Installs the custom log appender.function
+ * @param fun is the new log appender
+ */
+ extern void XklSetLogAppender( XklLogAppender fun );
+
+/**
+ * Sets maximum debug level.
+ * Message of the level more than the one set here - will be ignored
+ * @param level is a new debug level
+ */
+ extern void XklSetDebugLevel( int level );
+
+/**
+ * Output (optionally) some debug info
+ * @param level is a level of the message
+ * @param format is a format (like in printf)
+ * @see _XklDebug
+ */
+#define XklDebug( level, format, args... ) \
+ _XklDebug( __FILE__, __func__, level, format, ## args )
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/libxklavier/xklavier_config.c b/libxklavier/xklavier_config.c
new file mode 100644
index 0000000..9c705e3
--- /dev/null
+++ b/libxklavier/xklavier_config.c
@@ -0,0 +1,708 @@
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+#include <sys/stat.h>
+
+#include <libxml/xpath.h>
+
+#include "config.h"
+
+#include "xklavier_private.h"
+
+#include <X11/extensions/XKBfile.h>
+#include <X11/extensions/XKM.h>
+
+#define RULES_FILE "xfree86"
+
+#define RULES_PATH ( XKB_BASE "/rules/" RULES_FILE )
+
+#define XML_CFG_PATH ( XKB_BASE "/rules/xfree86.xml" )
+
+#define MULTIPLE_LAYOUTS_CHECK_PATH ( XKB_BASE "/symbols/pc/en_US" )
+
+#define XK_XKB_KEYS
+#include <X11/keysymdef.h>
+
+typedef struct _XklConfigRegistry
+{
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpathContext;
+}
+XklConfigRegistry;
+
+XkbRF_VarDefsRec _xklVarDefs;
+
+static XklConfigRegistry theRegistry;
+
+static xmlXPathCompExprPtr modelsXPath;
+static xmlXPathCompExprPtr layoutsXPath;
+static xmlXPathCompExprPtr optionGroupsXPath;
+
+static XkbRF_RulesPtr rules;
+static XkbComponentNamesRec componentNames;
+static char *locale;
+
+static Bool _XklConfigInitialized( )
+{
+ return theRegistry.xpathContext != NULL;
+}
+
+static xmlChar *_XklNodeGetXmlLangAttr( xmlNodePtr nptr )
+{
+ if( nptr->properties != NULL &&
+ !strcmp( "lang", nptr->properties[0].name ) &&
+ nptr->properties[0].ns != NULL &&
+ !strcmp( "xml", nptr->properties[0].ns->prefix ) &&
+ nptr->properties[0].children != NULL )
+ return nptr->properties[0].children->content;
+ else
+ return NULL;
+}
+
+static Bool _XklReadConfigItem( xmlNodePtr iptr, XklConfigItemPtr pci )
+{
+ xmlNodePtr nameElement, descElement = NULL, ntDescElement =
+ NULL, nptr, ptr, shortDescElement = NULL, ntShortDescElement = NULL;
+ int maxDescPriority = -1;
+ int maxShortDescPriority = -1;
+
+ *pci->name = 0;
+ *pci->shortDescription = 0;
+ *pci->description = 0;
+ if( iptr->type != XML_ELEMENT_NODE )
+ return False;
+ ptr = iptr->children;
+ while( ptr != NULL )
+ {
+ switch ( ptr->type )
+ {
+ case XML_ELEMENT_NODE:
+ if( !strcmp( ptr->name, "configItem" ) )
+ break;
+ return False;
+ case XML_TEXT_NODE:
+ ptr = ptr->next;
+ continue;
+ default:
+ return False;
+ }
+ break;
+ }
+ if( ptr == NULL )
+ return False;
+
+ nptr = ptr->children;
+
+ if( nptr->type == XML_TEXT_NODE )
+ nptr = nptr->next;
+ nameElement = nptr;
+ nptr = nptr->next;
+
+ while( nptr != NULL )
+ {
+ if( nptr->type != XML_TEXT_NODE )
+ {
+ xmlChar *lang = _XklNodeGetXmlLangAttr( nptr );
+
+ if( lang != NULL )
+ {
+ int priority = _XklGetLanguagePriority( lang );
+ if( !strcmp( nptr->name, "description" ) && ( priority > maxDescPriority ) ) // higher priority
+ {
+ descElement = nptr;
+ maxDescPriority = priority;
+ } else if( !strcmp( nptr->name, "shortDescription" ) && ( priority > maxShortDescPriority ) ) // higher priority
+ {
+ shortDescElement = nptr;
+ maxShortDescPriority = priority;
+ }
+ } else
+ {
+ if( !strcmp( nptr->name, "description" ) )
+ ntDescElement = nptr;
+ else if( !strcmp( nptr->name, "shortDescription" ) )
+ ntShortDescElement = nptr;
+ }
+ }
+ nptr = nptr->next;
+ }
+
+ // if no language-specific description found - use the ones without lang
+ if( descElement == NULL )
+ descElement = ntDescElement;
+
+ if( shortDescElement == NULL )
+ shortDescElement = ntShortDescElement;
+
+ //
+ // Actually, here we should have some code to find the correct localized description...
+ //
+
+ if( nameElement != NULL && nameElement->children != NULL )
+ strncat( pci->name, nameElement->children->content,
+ XKL_MAX_CI_NAME_LENGTH - 1 );
+
+ if( shortDescElement != NULL && shortDescElement->children != NULL )
+ strncat( pci->shortDescription,
+ _XklLocaleFromUtf8( shortDescElement->children->content ),
+ XKL_MAX_CI_SHORT_DESC_LENGTH - 1 );
+
+ if( descElement != NULL && descElement->children != NULL )
+ strncat( pci->description,
+ _XklLocaleFromUtf8( descElement->children->content ),
+ XKL_MAX_CI_DESC_LENGTH - 1 );
+ return True;
+}
+
+static void _XklConfigEnumFromNodeSet( xmlNodeSetPtr nodes,
+ ConfigItemProcessFunc func,
+ void *userData )
+{
+ int i;
+ if( nodes != NULL )
+ {
+ xmlNodePtr *theNodePtr = nodes->nodeTab;
+ for( i = nodes->nodeNr; --i >= 0; )
+ {
+ XklConfigItem ci;
+ if( _XklReadConfigItem( *theNodePtr, &ci ) )
+ func( &ci, userData );
+
+ theNodePtr++;
+ }
+ }
+}
+
+static void _XklConfigEnumSimple( xmlXPathCompExprPtr xpathCompExpr,
+ ConfigItemProcessFunc func, void *userData )
+{
+ xmlXPathObjectPtr xpathObj;
+
+ if( !_XklConfigInitialized( ) )
+ return;
+ xpathObj = xmlXPathCompiledEval( xpathCompExpr, theRegistry.xpathContext );
+ if( xpathObj != NULL )
+ {
+ _XklConfigEnumFromNodeSet( xpathObj->nodesetval, func, userData );
+ xmlXPathFreeObject( xpathObj );
+ }
+}
+
+static void _XklConfigEnumDirect( const char *format,
+ const char *value,
+ ConfigItemProcessFunc func, void *userData )
+{
+ char xpathExpr[1024];
+ xmlXPathObjectPtr xpathObj;
+
+ if( !_XklConfigInitialized( ) )
+ return;
+ snprintf( xpathExpr, sizeof xpathExpr, format, value );
+ xpathObj = xmlXPathEval( xpathExpr, theRegistry.xpathContext );
+ if( xpathObj != NULL )
+ {
+ _XklConfigEnumFromNodeSet( xpathObj->nodesetval, func, userData );
+ xmlXPathFreeObject( xpathObj );
+ }
+}
+
+static Bool _XklConfigFindObject( const char *format,
+ const char *arg1,
+ XklConfigItemPtr ptr /* in/out */ ,
+ xmlNodePtr * nodePtr /* out */ )
+{
+ xmlXPathObjectPtr xpathObj;
+ xmlNodeSetPtr nodes;
+ Bool rv = False;
+ char xpathExpr[1024];
+
+ if( !_XklConfigInitialized( ) )
+ return False;
+
+ snprintf( xpathExpr, sizeof xpathExpr, format, arg1, ptr->name );
+ xpathObj = xmlXPathEval( xpathExpr, theRegistry.xpathContext );
+ if( xpathObj == NULL )
+ return False;
+
+ nodes = xpathObj->nodesetval;
+ if( nodes != NULL && nodes->nodeTab != NULL )
+ {
+ rv = _XklReadConfigItem( *nodes->nodeTab, ptr );
+ if( nodePtr != NULL )
+ {
+ *nodePtr = *nodes->nodeTab;
+ }
+ }
+
+ xmlXPathFreeObject( xpathObj );
+ return rv;
+}
+
+char *_XklConfigRecMergeLayouts( const XklConfigRecPtr data )
+{
+ return _XklConfigRecMergeByComma( ( const char ** ) data->layouts,
+ data->numLayouts );
+}
+
+char *_XklConfigRecMergeVariants( const XklConfigRecPtr data )
+{
+ return _XklConfigRecMergeByComma( ( const char ** ) data->variants,
+ data->numVariants );
+}
+
+char *_XklConfigRecMergeOptions( const XklConfigRecPtr data )
+{
+ return _XklConfigRecMergeByComma( ( const char ** ) data->options,
+ data->numOptions );
+}
+
+char *_XklConfigRecMergeByComma( const char **array, const int arrayLength )
+{
+ int len = 0;
+ int i;
+ char *merged;
+ const char **theString;
+
+ if( ( theString = array ) == NULL )
+ return NULL;
+
+ for( i = arrayLength; --i >= 0; theString++ )
+ {
+ if( *theString != NULL )
+ len += strlen( *theString );
+ len++;
+ }
+
+ if( len < 1 )
+ return NULL;
+
+ merged = ( char * ) malloc( len );
+ merged[0] = '\0';
+
+ theString = array;
+ for( i = arrayLength; --i >= 0; theString++ )
+ {
+ if( *theString != NULL )
+ strcat( merged, *theString );
+ if( i != 0 )
+ strcat( merged, "," );
+ }
+ return merged;
+}
+
+void _XklConfigRecSplitLayouts( XklConfigRecPtr data, const char *merged )
+{
+ _XklConfigRecSplitByComma( &data->layouts, &data->numLayouts, merged );
+}
+
+void _XklConfigRecSplitVariants( XklConfigRecPtr data, const char *merged )
+{
+ _XklConfigRecSplitByComma( &data->variants, &data->numVariants, merged );
+}
+
+void _XklConfigRecSplitOptions( XklConfigRecPtr data, const char *merged )
+{
+ _XklConfigRecSplitByComma( &data->options, &data->numOptions, merged );
+}
+
+void _XklConfigRecSplitByComma( char ***array,
+ int *arraySize, const char *merged )
+{
+ const char *pc = merged;
+ char **ppc, *npc;
+ *arraySize = 0;
+ *array = NULL;
+
+ if( merged == NULL || merged[0] == '\0' )
+ return;
+
+ // first count the elements
+ while( ( npc = strchr( pc, ',' ) ) != NULL )
+ {
+ ( *arraySize )++;
+ pc = npc + 1;
+ }
+ ( *arraySize )++;
+
+ if( ( *arraySize ) != 0 )
+ {
+ int len;
+ *array = ( char ** ) malloc( ( sizeof( char * ) ) * ( *arraySize ) );
+
+ ppc = *array;
+ pc = merged;
+ while( ( npc = strchr( pc, ',' ) ) != NULL )
+ {
+ int len = npc - pc;
+ *ppc = ( char * ) strndup( pc, len );
+ ppc++;
+ pc = npc + 1;
+ }
+
+ len = npc - pc;
+ *ppc = ( char * ) strndup( pc, len );
+ }
+}
+
+static Bool _XklConfigPrepareBeforeKbd( const XklConfigRecPtr data )
+{
+ memset( &_xklVarDefs, 0, sizeof( _xklVarDefs ) );
+
+ _xklVarDefs.model = ( char * ) data->model;
+
+ if( data->layouts != NULL )
+ _xklVarDefs.layout = _XklConfigRecMergeLayouts( data );
+
+ if( data->variants != NULL )
+ _xklVarDefs.variant = _XklConfigRecMergeVariants( data );
+
+ if( data->options != NULL )
+ _xklVarDefs.options = _XklConfigRecMergeOptions( data );
+
+ locale = setlocale( LC_ALL, NULL );
+ if( locale != NULL )
+ locale = strdup( locale );
+
+ rules = XkbRF_Load( RULES_PATH, locale, True, True );
+
+ if( rules == NULL )
+ {
+ _xklLastErrorMsg = "Could not load rules";
+ return False;
+ }
+
+ if( !XkbRF_GetComponents( rules, &_xklVarDefs, &componentNames ) )
+ {
+ _xklLastErrorMsg = "Could not translate rules into components";
+ return False;
+ }
+
+ return True;
+}
+
+static void _XklConfigCleanAfterKbd( )
+{
+ XkbRF_Free( rules, True );
+
+ if( locale != NULL )
+ {
+ free( locale );
+ locale = NULL;
+ }
+ if( _xklVarDefs.layout != NULL )
+ {
+ free( _xklVarDefs.layout );
+ _xklVarDefs.layout = NULL;
+ }
+ if( _xklVarDefs.options != NULL )
+ {
+ free( _xklVarDefs.options );
+ _xklVarDefs.options = NULL;
+ }
+}
+
+static void _XklApplyFun2XkbDesc( XkbDescPtr xkb, XkbDescModifierFunc fun,
+ void *userData, Bool activeInServer )
+{
+ int mask;
+ // XklDumpXkbDesc( "comp.xkb", xkb );
+ if( fun == NULL )
+ return;
+
+ if( activeInServer )
+ {
+ mask = ( *fun ) ( NULL, NULL );
+ if( mask == 0 )
+ return;
+ XkbGetUpdatedMap( _xklDpy, mask, xkb );
+ // XklDumpXkbDesc( "restored1.xkb", xkb );
+ }
+
+ mask = ( *fun ) ( xkb, userData );
+ if( activeInServer )
+ {
+ // XklDumpXkbDesc( "comp+.xkb", xkb );
+ XkbSetMap( _xklDpy, mask, xkb );
+ XSync( _xklDpy, False );
+
+ // XkbGetUpdatedMap( _xklDpy, XkbAllMapComponentsMask, xkb );
+ // XklDumpXkbDesc( "restored2.xkb", xkb );
+ }
+}
+
+void XklConfigInit( void )
+{
+ xmlXPathInit( );
+ modelsXPath = xmlXPathCompile( "/xkbConfigRegistry/modelList/model" );
+ layoutsXPath = xmlXPathCompile( "/xkbConfigRegistry/layoutList/layout" );
+ optionGroupsXPath =
+ xmlXPathCompile( "/xkbConfigRegistry/optionList/group" );
+ _XklI18NInit( );
+}
+
+void XklConfigTerm( void )
+{
+ if( modelsXPath != NULL )
+ xmlXPathFreeCompExpr( modelsXPath );
+ if( layoutsXPath != NULL )
+ xmlXPathFreeCompExpr( layoutsXPath );
+ if( optionGroupsXPath != NULL )
+ xmlXPathFreeCompExpr( optionGroupsXPath );
+}
+
+Bool XklConfigLoadRegistry( void )
+{
+ theRegistry.doc = xmlParseFile( XML_CFG_PATH );
+ if( theRegistry.doc == NULL )
+ {
+ theRegistry.xpathContext = NULL;
+ _xklLastErrorMsg = "Could not parse XKB configuration registry";
+ } else
+ theRegistry.xpathContext = xmlXPathNewContext( theRegistry.doc );
+ return _XklConfigInitialized( );
+}
+
+void XklConfigFreeRegistry( void )
+{
+ if( _XklConfigInitialized( ) )
+ {
+ xmlXPathFreeContext( theRegistry.xpathContext );
+ xmlFreeDoc( theRegistry.doc );
+ }
+}
+
+void XklConfigEnumModels( ConfigItemProcessFunc func, void *userData )
+{
+ _XklConfigEnumSimple( modelsXPath, func, userData );
+}
+
+void XklConfigEnumLayouts( ConfigItemProcessFunc func, void *userData )
+{
+ _XklConfigEnumSimple( layoutsXPath, func, userData );
+}
+
+void XklConfigEnumLayoutVariants( const char *layoutName,
+ ConfigItemProcessFunc func, void *userData )
+{
+ _XklConfigEnumDirect
+ ( "/xkbConfigRegistry/layoutList/layout/variantList/variant[../../configItem/name = '%s']",
+ layoutName, func, userData );
+}
+
+void XklConfigEnumOptionGroups( GroupProcessFunc func, void *userData )
+{
+ xmlXPathObjectPtr xpathObj;
+ int i;
+
+ if( !_XklConfigInitialized( ) )
+ return;
+ xpathObj =
+ xmlXPathCompiledEval( optionGroupsXPath, theRegistry.xpathContext );
+ if( xpathObj != NULL )
+ {
+ xmlNodeSetPtr nodes = xpathObj->nodesetval;
+ xmlNodePtr *theNodePtr = nodes->nodeTab;
+ for( i = nodes->nodeNr; --i >= 0; )
+ {
+ XklConfigItem ci;
+
+ if( _XklReadConfigItem( *theNodePtr, &ci ) )
+ {
+ Bool allowMC = True;
+ xmlChar *allowMCS =
+ xmlGetProp( *theNodePtr, "allowMultipleSelection" );
+ if( allowMCS != NULL )
+ {
+ allowMC = strcmp( "false", allowMCS );
+ xmlFree( allowMCS );
+ }
+
+ func( &ci, allowMC, userData );
+ }
+
+ theNodePtr++;
+ }
+ xmlXPathFreeObject( xpathObj );
+ }
+}
+
+void XklConfigEnumOptions( const char *optionGroupName,
+ ConfigItemProcessFunc func, void *userData )
+{
+ _XklConfigEnumDirect
+ ( "/xkbConfigRegistry/optionList/group/option[../configItem/name = '%s']",
+ optionGroupName, func, userData );
+}
+
+Bool XklConfigFindModel( XklConfigItemPtr ptr /* in/out */ )
+{
+ return
+ _XklConfigFindObject
+ ( "/xkbConfigRegistry/modelList/model[configItem/name = '%s%s']", "",
+ ptr, NULL );
+}
+
+Bool XklConfigFindLayout( XklConfigItemPtr ptr /* in/out */ )
+{
+ return
+ _XklConfigFindObject
+ ( "/xkbConfigRegistry/layoutList/layout[configItem/name = '%s%s']", "",
+ ptr, NULL );
+}
+
+Bool XklConfigFindVariant( const char *layoutName,
+ XklConfigItemPtr ptr /* in/out */ )
+{
+ return
+ _XklConfigFindObject
+ ( "/xkbConfigRegistry/layoutList/layout/variantList/variant"
+ "[../../configItem/name = '%s' and configItem/name = '%s']",
+ layoutName, ptr, NULL );
+}
+
+Bool XklConfigFindOptionGroup( XklConfigItemPtr ptr /* in/out */ ,
+ Bool * allowMultipleSelection /* out */ )
+{
+ xmlNodePtr node;
+ Bool rv =
+ _XklConfigFindObject
+ ( "/xkbConfigRegistry/optionList/group[configItem/name = '%s%s']", "",
+ ptr, &node );
+
+ if( rv && allowMultipleSelection != NULL )
+ {
+ xmlChar *val = xmlGetProp( node, "allowMultipleSelection" );
+ *allowMultipleSelection = False;
+ if( val != NULL )
+ {
+ *allowMultipleSelection = !strcmp( val, "true" );
+ xmlFree( val );
+ }
+ }
+ return rv;
+}
+
+Bool XklConfigFindOption( const char *optionGroupName,
+ XklConfigItemPtr ptr /* in/out */ )
+{
+ return
+ _XklConfigFindObject
+ ( "/xkbConfigRegistry/optionList/group/option"
+ "[../configItem/name = '%s' and configItem/name = '%s']",
+ optionGroupName, ptr, NULL );
+}
+
+Bool XklMultipleLayoutsSupported( void )
+{
+ struct stat buf;
+ return 0 == stat( MULTIPLE_LAYOUTS_CHECK_PATH, &buf );
+}
+
+Bool XklConfigActivate( const XklConfigRecPtr data, XkbDescModifierFunc fun,
+ void *userData )
+{
+ Bool rv = False;
+ if( _XklConfigPrepareBeforeKbd( data ) )
+ {
+ XkbDescPtr xkb;
+ xkb =
+ XkbGetKeyboardByName( _xklDpy, XkbUseCoreKbd, &componentNames,
+ XkbGBN_AllComponentsMask,
+ XkbGBN_AllComponentsMask &
+ ( ~XkbGBN_GeometryMask ), True );
+
+ //!! Do I need to free it anywhere?
+ if( xkb != NULL )
+ {
+ _XklApplyFun2XkbDesc( xkb, fun, userData, True );
+#if 0
+ XklDumpXkbDesc( "config.xkb", xkb );
+ int i;
+ XklDebug( 150, "New model: [%s]\n", data->model );
+ XklDebug( 150, "New layout: [%s]\n", data->layout );
+ XklDebug( 150, "New variant: [%s]\n", data->variant );
+ for( i = data->numOptions; --i >= 0; )
+ XklDebug( 150, "New option[%d]: [%s]\n", i, data->options[i] );
+#endif
+
+ if( XklSetNamesProp
+ ( _xklAtoms[XKB_RF_NAMES_PROP_ATOM], RULES_FILE, data ) )
+ rv = True;
+ else
+ _xklLastErrorMsg = "Could not set names property";
+ XkbFreeKeyboard( xkb, XkbAllComponentsMask, True );
+ } else
+ {
+ _xklLastErrorMsg = "Could not load keyboard description";
+ }
+ }
+ _XklConfigCleanAfterKbd( );
+ return rv;
+}
+
+int XklSetKeyAsSwitcher( XkbDescPtr kbd, void *userData )
+{
+ if( kbd != NULL )
+ {
+ XkbClientMapPtr map = kbd->map;
+ if( map != NULL )
+ {
+ KeySym keysym = ( KeySym ) userData;
+ KeySym *psym = map->syms;
+ int symno;
+
+ for( symno = map->num_syms; --symno >= 0; psym++ )
+ {
+ if( *psym == keysym )
+ {
+ XklDebug( 160, "Changing %s to %s at %d\n",
+ XKeysymToString( *psym ),
+ XKeysymToString( XK_ISO_Next_Group ), psym - map->syms );
+ *psym = XK_ISO_Next_Group;
+ break;
+ }
+ }
+ } else
+ XklDebug( 160, "No client map in the keyboard description?\n" );
+ }
+ return XkbKeySymsMask | XkbKeyTypesMask | XkbKeyActionsMask;
+}
+
+Bool XklConfigWriteXKMFile( const char *fileName, const XklConfigRecPtr data,
+ XkbDescModifierFunc fun, void *userData )
+{
+ Bool rv = False;
+
+ FILE *output = fopen( fileName, "w" );
+ XkbFileInfo dumpInfo;
+
+ if( output == NULL )
+ {
+ _xklLastErrorMsg = "Could not open the XKB file";
+ return False;
+ }
+
+ if( _XklConfigPrepareBeforeKbd( data ) )
+ {
+ XkbDescPtr xkb;
+ xkb =
+ XkbGetKeyboardByName( _xklDpy, XkbUseCoreKbd, &componentNames,
+ XkbGBN_AllComponentsMask,
+ XkbGBN_AllComponentsMask &
+ ( ~XkbGBN_GeometryMask ), False );
+ if( xkb != NULL )
+ {
+ _XklApplyFun2XkbDesc( xkb, fun, userData, False );
+
+ dumpInfo.defined = 0;
+ dumpInfo.xkb = xkb;
+ dumpInfo.type = XkmKeymapFile;
+ rv = XkbWriteXKMFile( output, &dumpInfo );
+ XkbFreeKeyboard( xkb, XkbGBN_AllComponentsMask, True );
+ } else
+ _xklLastErrorMsg = "Could not load keyboard description";
+ }
+ _XklConfigCleanAfterKbd( );
+ fclose( output );
+ return rv;
+}
diff --git a/libxklavier/xklavier_config.h b/libxklavier/xklavier_config.h
new file mode 100644
index 0000000..18fd71d
--- /dev/null
+++ b/libxklavier/xklavier_config.h
@@ -0,0 +1,389 @@
+/**
+ * @file xklavier_config.h
+ */
+
+#ifndef __XKLABIER_CONFIG_H__
+#define __XKLABIER_CONFIG_H__
+
+#include <libxklavier/xklavier.h>
+
+/**
+ * Maximum name length, including '\'0' character
+ */
+#define XKL_MAX_CI_NAME_LENGTH 32
+
+/**
+ * Maximum short description length, including '\\0' character.
+ * Important: this length is in bytes, so for unicode (UTF-8 encoding in
+ * XML file) the actual maximum length can be smaller.
+ */
+#define XKL_MAX_CI_SHORT_DESC_LENGTH 10
+
+/**
+ * Maximum description length, including '\\0' character.
+ * Important: this length is in bytes, so for unicode (UTF-8 encoding in
+ * XML file) the actual maximum length can be smaller.
+ */
+#define XKL_MAX_CI_DESC_LENGTH 192
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/**
+ * The configuration item. Corresponds to XML element "configItem".
+ */
+ typedef struct _XklConfigItem
+ {
+/**
+ * The configuration item name. Corresponds to XML element "name".
+ */
+ char name[XKL_MAX_CI_NAME_LENGTH];
+
+/**
+ * The configuration item short description. Corresponds to XML element "shortDescription".
+ */
+ char shortDescription[XKL_MAX_CI_DESC_LENGTH];
+
+/**
+ * The configuration item description. Corresponds to XML element "description".
+ */
+ char description[XKL_MAX_CI_DESC_LENGTH];
+ }
+ XklConfigItem, *XklConfigItemPtr;
+
+/**
+ * Basic configuration params
+ */
+ typedef struct _XklConfigRec
+ {
+/**
+ * The keyboard model
+ */
+ char *model;
+/**
+ * The number of keyboard layouts
+ */
+ int numLayouts;
+/**
+ * The array of keyboard layouts
+ */
+ char **layouts;
+/**
+ * The number of keyboard layout variants
+ */
+ int numVariants;
+/**
+ * The array of keyboard layout variants (if any)
+ */
+ char **variants;
+/**
+ * The number of keyboard layout options
+ */
+ int numOptions;
+/**
+ * The array of keyboard layout options
+ */
+ char **options;
+ }
+ XklConfigRec, *XklConfigRecPtr;
+/**
+ * @defgroup xklconfiginitterm XKB configuration handling initialization and termination
+ * @{
+ */
+
+/**
+ * Initializes XML configuration-related structures
+ */
+ extern void XklConfigInit( void );
+
+/**
+ * Cleans XML configuration-related structures
+ */
+ extern void XklConfigTerm( void );
+
+/**
+ * Loads XML configuration registry
+ * @return true on success
+ */
+ extern Bool XklConfigLoadRegistry( void );
+
+/**
+ * Frees XML configuration registry
+ */
+ extern void XklConfigFreeRegistry( void );
+/** @} */
+
+/**
+ * @defgroup enum XKB configuration elements enumeration functions
+ * @{
+ */
+
+/**
+ * Callback type used for enumerating keyboard models, layouts, variants, options
+ * @param configItem is the item from registry
+ * @param userData is anything which can be stored into the pointer
+ */
+ typedef void ( *ConfigItemProcessFunc ) ( const XklConfigItemPtr configItem,
+ void *userData );
+
+/**
+ * Callback type used for enumerating keyboard option groups
+ * @param configItem is the item from registry
+ * @param allowMultipleSelection is a flag whether this group allows multiple selection
+ * @param userData is anything which can be stored into the pointer
+ */
+ typedef void ( *GroupProcessFunc ) ( const XklConfigItemPtr configItem,
+ Bool allowMultipleSelection,
+ void *userData );
+/**
+ * Callback used to modify/patch the keyboard description before the
+ * activation. The function should be able to work without kbd ( kbd = NULL ).
+ * In this case, it should just return the mask of possible changes.
+ * @param kbd is the keyboard description
+ * @param userData is anything which can be stored into the pointer
+ * @return the mask of the changes
+ */
+ typedef int ( *XkbDescModifierFunc ) ( XkbDescPtr kbd, void *userData );
+/**
+ * Enumerates keyboard models from the XML configuration registry
+ * @param func is a callback to call for every model
+ * @param userData is anything which can be stored into the pointer
+ */
+ extern void XklConfigEnumModels( ConfigItemProcessFunc func,
+ void *userData );
+
+/**
+ * Enumerates keyboard layouts from the XML configuration registry
+ * @param func is a callback to call for every layout
+ * @param userData is anything which can be stored into the pointer
+ */
+ extern void XklConfigEnumLayouts( ConfigItemProcessFunc func,
+ void *userData );
+
+/**
+ * Enumerates keyboard layout variants from the XML configuration registry
+ * @param layoutName is the layout name for which variants will be listed
+ * @param func is a callback to call for every layout variant
+ * @param userData is anything which can be stored into the pointer
+ */
+ extern void XklConfigEnumLayoutVariants( const char *layoutName,
+ ConfigItemProcessFunc func,
+ void *userData );
+
+/**
+ * Enumerates keyboard option groups from the XML configuration registry
+ * @param func is a callback to call for every option group
+ * @param userData is anything which can be stored into the pointer
+ */
+ extern void XklConfigEnumOptionGroups( GroupProcessFunc func,
+ void *userData );
+
+/**
+ * Enumerates keyboard options from the XML configuration registry
+ * @param optionGroupName is the option group name for which variants
+ * will be listed
+ * @param func is a callback to call for every option
+ * @param userData is anything which can be stored into the pointer
+ */
+ extern void XklConfigEnumOptions( const char *optionGroupName,
+ ConfigItemProcessFunc func,
+ void *userData );
+
+/** @} */
+
+/**
+ * @defgroup lookup XKB configuration element lookup functions
+ * @{
+ */
+
+/**
+ * Loads a keyboard model information from the XML configuration registry.
+ * @param ptr is a pointer to a XklConfigItem containing the name of the
+ * keyboard model. On successfull return, the descriptions are filled.
+ * @return True if appropriate element was found and loaded
+ */
+ extern Bool XklConfigFindModel( XklConfigItemPtr ptr );
+
+/**
+ * Loads a keyboard layout information from the XML configuration registry.
+ * @param ptr is a pointer to a XklConfigItem containing the name of the
+ * keyboard layout. On successfull return, the descriptions are filled.
+ * @return True if appropriate element was found and loaded
+ */
+ extern Bool XklConfigFindLayout( XklConfigItemPtr ptr );
+
+/**
+ * Loads a keyboard layout variant information from the XML configuration
+ * registry.
+ * @param layoutName is a name of the parent layout
+ * @param ptr is a pointer to a XklConfigItem containing the name of the
+ * keyboard layout variant. On successfull return, the descriptions are filled.
+ * @return True if appropriate element was found and loaded
+ */
+ extern Bool XklConfigFindVariant( const char *layoutName,
+ XklConfigItemPtr ptr );
+
+/**
+ * Loads a keyboard option group information from the XML configuration
+ * registry.
+ * @param ptr is a pointer to a XklConfigItem containing the name of the
+ * keyboard option group. On successfull return, the descriptions are filled.
+ * @param allowMultipleSelection is a pointer to some Bool variable to fill
+ * the corresponding attribute of XML element "group".
+ * @return True if appropriate element was found and loaded
+ */
+ extern Bool XklConfigFindOptionGroup( XklConfigItemPtr ptr,
+ Bool * allowMultipleSelection );
+
+/**
+ * Loads a keyboard option information from the XML configuration
+ * registry.
+ * @param optionGroupName is a name of the option group
+ * @param ptr is a pointer to a XklConfigItem containing the name of the
+ * keyboard option. On successfull return, the descriptions are filled.
+ * @return True if appropriate element was found and loaded
+ */
+ extern Bool XklConfigFindOption( const char *optionGroupName,
+ XklConfigItemPtr ptr );
+/** @} */
+
+/**
+ * @defgroup activation XKB configuration activation
+ * @{
+ */
+
+/**
+ * Determines whether multiple layouts (by Ivan Pascal) are supported.
+ * @return True if so.
+ */
+ extern Bool XklMultipleLayoutsSupported( void );
+/**
+ * Activates some XKB configuration
+ * @param data is a valid XKB configuration
+ * @param fun is a callback function for modifying the XKB keyboard
+ * description. Can be NULL
+ * @param userData is a data to pass to the callback
+ * @return True on success
+ * @see XklSetKeyAsSwitcher
+ * At the moment, accepts only _ONE_ layout. Later probably I'll improve this..
+ */
+ extern Bool XklConfigActivate( const XklConfigRecPtr data,
+ XkbDescModifierFunc fun, void *userData );
+
+/**
+ * One of possible XkbDescModifierFunc
+ * Handy for setting one key as a group switcher
+ * @param kbd is the keyboard to modify
+ * @param userData is a keysym to make a group switcher
+ */
+ extern int XklSetKeyAsSwitcher( XkbDescPtr kbd, void *userData );
+
+/**
+ * Loads the current XKB configuration (from X server)
+ * @param data is a buffer for XKB configuration
+ * @return True on success
+ */
+ extern Bool XklConfigGetFromServer( XklConfigRecPtr data );
+
+/**
+ * Loads the current XKB configuration (from backup)
+ * @param data is a buffer for XKB configuration
+ * @return True on success
+ * @see XklBackupNamesProp
+ */
+ extern Bool XklConfigGetFromBackup( XklConfigRecPtr data );
+
+/**
+ * Writes some XKB configuration into XKM file
+ * @param fileName is a name of the file to create
+ * @param data is a valid XKB configuration
+ * @param fun is a callback function for modifying the XKB keyboard
+ * description. Can be NULL
+ * @param userData is a data to pass to the callback
+ * @return True on success
+ * At the moment, accepts only _ONE_ layout. Later probably I'll improve this..
+ */
+ extern Bool XklConfigWriteXKMFile( const char *fileName,
+ const XklConfigRecPtr data,
+ XkbDescModifierFunc fun,
+ void *userData );
+
+/** @} */
+
+/**
+ * @defgroup props Saving and restoring XKB configuration into X root window properties
+ * Generalizes XkbRF_GetNamesProp and XkbRF_SetNamesProp.
+ * @{
+ */
+
+/**
+ * Gets the XKB configuration from any root window property
+ * @param rulesAtomName is an atom name of the root window property to read
+ * @param rulesFileOut is a pointer to hold the file name
+ * @param configOut is a buffer to hold the result -
+ * all records are allocated using standard malloc
+ * @return True on success
+ */
+ extern Bool XklGetNamesProp( Atom rulesAtomName,
+ char **rulesFileOut,
+ XklConfigRecPtr configOut );
+
+/**
+ * Saves the XKB configuration into any root window property
+ * @param rulesAtomName is an atom name of the root window property to write
+ * @param rulesFile is a rules file name
+ * @param config is a configuration to save
+ * @return True on success
+ */
+ extern Bool XklSetNamesProp( Atom rulesAtomName,
+ char *rulesFile, XklConfigRecPtr config );
+
+/**
+ * Backups current XKB configuration into some property -
+ * if this property is not defined yet.
+ * @return True on success
+ */
+ extern Bool XklBackupNamesProp( );
+
+/**
+ * Restores XKB from the property saved by XklBackupNamesProp
+ * @return True on success
+ * @see XklBackupNamesProp
+ */
+ extern Bool XklRestoreNamesProp( );
+
+/** @} */
+
+/**
+ * @defgroup xklconfig XklConfigRec management utilities
+ * Little utilities for initing/destroying/resetting XklConfigRec.
+ * @{
+ */
+
+/**
+ * Initializes the record (actually, fills it with 0-s
+ * @param data is a record to initialize
+ */
+ extern void XklConfigRecInit( XklConfigRecPtr data );
+
+/**
+ * Resets the record (equal to Destroy and Init)
+ * @param data is a record to reset
+ */
+ extern void XklConfigRecReset( XklConfigRecPtr data );
+
+/**
+ * Cleans the record (frees all the non-null members)
+ * @param data is a record to clean
+ */
+ extern void XklConfigRecDestroy( XklConfigRecPtr data );
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/libxklavier/xklavier_config_i18n.c b/libxklavier/xklavier_config_i18n.c
new file mode 100644
index 0000000..c8eef15
--- /dev/null
+++ b/libxklavier/xklavier_config_i18n.c
@@ -0,0 +1,237 @@
+#include <stdlib.h>
+#include <string.h>
+#include <iconv.h>
+
+#include "config.h"
+
+#ifdef HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+#endif
+
+#ifdef HAVE_SETLOCALE
+# include <locale.h>
+#endif
+
+#include "xklavier_private.h"
+
+#define MAX_LOCALE_LEN 128
+
+static char localeSubStrings[3][MAX_LOCALE_LEN];
+
+/*
+ * some bad guys create LC_ALL=LC_CTYPE=ru_RU.UTF-8;LC_NUMERIC=C;LC_TIME=ru_RU.UTF-8;LC_COLLATE=ru_RU.UTF-8;LC_MONETARY=ru_RU.UTF-8;LC_MESSAGES=ru_RU.UTF-8;LC_PAPER=ru_RU.UTF-8;LC_NAME=ru_RU.UTF-8;LC_ADDRESS=ru_RU.UTF-8;LC_TELEPHONE=ru_RU.UTF-8;LC_MEASUREMENT=ru_RU.UTF-8;LC_IDENTIFICATION=ru_RU.UTF-8
+ */
+static const char *_XklParseLC_ALL2LC_MESSAGES( const char *lcAll )
+{
+ const char *lcMsgPos = strstr( lcAll, "LC_MESSAGES=" );
+ const char *lcMsgEnd;
+ size_t len;
+ static char buf[128];
+ if( lcMsgPos == NULL )
+ return lcAll;
+ lcMsgPos += 12;
+ lcMsgEnd = strchr( lcMsgPos, ';' );
+ if( lcMsgEnd == NULL ) // LC_MESSAGES is the last piece of LC_ALL
+ {
+ return lcMsgPos; //safe to return!
+ }
+ len = lcMsgEnd - lcMsgPos;
+ if( len > sizeof( buf ) )
+ len = sizeof( buf );
+ strncpy( buf, lcMsgPos, len );
+ buf[sizeof( buf ) - 1] = '\0';
+ return buf;
+}
+
+// Taken from gnome-vfs
+static Bool _XklGetCharset( const char **a )
+{
+ static const char *charset = NULL;
+
+ if( charset == NULL )
+ {
+ charset = getenv( "CHARSET" );
+
+ if( charset == NULL || charset[0] == '\0' )
+ {
+// taken from gnome-vfs
+#ifdef HAVE_LANGINFO_CODESET
+ charset = nl_langinfo( CODESET );
+ if( charset == NULL || charset[0] == '\0' )
+ {
+#endif
+#ifdef HAVE_SETLOCALE
+ charset = setlocale( LC_CTYPE, NULL );
+ if( charset == NULL || charset[0] == '\0' )
+ {
+#endif
+ charset = getenv( "LC_ALL" );
+ if( charset == NULL || charset[0] == '\0' )
+ {
+ charset = getenv( "LC_CTYPE" );
+ if( charset == NULL || charset[0] == '\0' )
+ charset = getenv( "LANG" );
+ }
+#ifdef HAVE_SETLOCALE
+ } else
+ {
+ XklDebug( 150, "Using charset from setlocale: [%s]\n", charset );
+ }
+#endif
+#ifdef HAVE_LANGINFO_CODESET
+ } else
+ {
+ XklDebug( 150, "Using charset from nl_langinfo: [%s]\n", charset );
+ }
+#endif
+ }
+ }
+
+ if( charset != NULL && *charset != '\0' )
+ {
+ *a = charset;
+ return ( charset != NULL && strstr( charset, "UTF-8" ) != NULL );
+ }
+ /* Assume this for compatibility at present. */
+ *a = "US-ASCII";
+ XklDebug( 150, "Using charset fallback: [%s]\n", *a );
+
+ return False;
+}
+
+char *_XklLocaleFromUtf8( const char *utf8string )
+{
+ int len;
+ int bytesRead;
+ int bytesWritten;
+
+ iconv_t converter;
+ static char converted[XKL_MAX_CI_DESC_LENGTH];
+ char *convertedStart = converted;
+ char *utfStart = ( char * ) utf8string;
+ int clen = XKL_MAX_CI_DESC_LENGTH - 1;
+ const char *charset;
+
+ static Bool alreadyWarned = False;
+
+ if( utf8string == NULL )
+ return NULL;
+
+ len = strlen( utf8string );
+
+ if( _XklGetCharset( &charset ) )
+ return strdup( utf8string );
+
+ converter = iconv_open( charset, "UTF-8" );
+ if( converter == ( iconv_t ) - 1 )
+ {
+ if( !alreadyWarned )
+ {
+ alreadyWarned = True;
+ XklDebug( 0,
+ "Unable to convert MIME info from UTF-8 to the current locale %s. MIME info will probably display wrong.",
+ charset );
+ }
+ return strdup( utf8string );
+ }
+ //converted = convert_with_iconv( utf8string,
+ // len, converter, &bytesRead, &bytesWritten );
+
+ if( iconv( converter, &utfStart, &len, &convertedStart, &clen ) == -1 )
+ {
+ XklDebug( 0,
+ "Unable to convert %s from UTF-8 to %s, this string will probably display wrong.",
+ utf8string, charset );
+ return strdup( utf8string );
+ }
+ *convertedStart = '\0';
+
+ iconv_close( converter );
+
+ return converted;
+}
+
+/*
+ * country[_LANG[.ENCODING]] - any other ideas?
+ */
+void _XklI18NInit( )
+{
+ char *dotPos;
+ char *underscorePos;
+ const char *locale = NULL;
+ char *curSubstring;
+
+ localeSubStrings[0][0] = localeSubStrings[1][0] =
+ localeSubStrings[2][0] = '\0';
+
+#ifdef HAVE_SETLOCALE
+ locale = setlocale( LC_MESSAGES, NULL );
+#endif
+ if( locale == NULL || locale[0] == '\0' )
+ {
+ locale = getenv( "LC_MESSAGES" );
+ if( locale == NULL || locale[0] == '\0' )
+ {
+ locale = getenv( "LC_ALL" );
+ if( locale == NULL || locale[0] == '\0' )
+ locale = getenv( "LANG" );
+ else
+ locale = _XklParseLC_ALL2LC_MESSAGES( locale );
+ }
+ }
+
+ if( locale == NULL )
+ {
+ XklDebug( 0, "Could not find locale - can be problems with i18n" );
+ return;
+ }
+
+ strncpy( localeSubStrings[0], locale, MAX_LOCALE_LEN );
+
+ curSubstring = localeSubStrings[1];
+
+ dotPos = strchr( locale, '.' );
+ if( dotPos != NULL )
+ {
+ int idx = dotPos - locale;
+ if( idx >= MAX_LOCALE_LEN )
+ idx = MAX_LOCALE_LEN - 1;
+ strncpy( curSubstring, locale, idx );
+ curSubstring[idx] = '\0';
+ curSubstring += MAX_LOCALE_LEN;
+ }
+
+ underscorePos = strchr( locale, '_' );
+ if( underscorePos != NULL && ( dotPos == NULL || dotPos > underscorePos ) )
+ {
+ int idx = underscorePos - locale;
+ if( idx >= MAX_LOCALE_LEN )
+ idx = MAX_LOCALE_LEN - 1;
+ strncpy( curSubstring, locale, idx );
+ curSubstring[idx] = '\0';
+ }
+
+ XklDebug( 150, "Locale search order:\n" );
+ XklDebug( 150, " 0: %s\n", localeSubStrings[0] ); // full locale - highest priority
+ XklDebug( 150, " 1: %s\n", localeSubStrings[1] );
+ XklDebug( 150, " 2: %s\n", localeSubStrings[2] );
+}
+
+int _XklGetLanguagePriority( const char *lang )
+{
+ int i, priority = -1;
+
+ for( i = sizeof( localeSubStrings ) / sizeof( localeSubStrings[0] );
+ --i >= 0; )
+ {
+ if( localeSubStrings[0][0] == '\0' )
+ continue;
+
+ if( !strcmp( lang, localeSubStrings[i] ) )
+ {
+ priority = i;
+ break;
+ }
+ }
+ return priority;
+}
diff --git a/libxklavier/xklavier_dump.c b/libxklavier/xklavier_dump.c
new file mode 100644
index 0000000..bc7ab08
--- /dev/null
+++ b/libxklavier/xklavier_dump.c
@@ -0,0 +1,276 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "xklavier_private.h"
+
+static void _XkbModsRecDump( FILE * fs, XkbModsRec * mods )
+{
+ fprintf( fs, "flags: 0x%X\n", mods->mask );
+ fprintf( fs, "real_mods: 0x%X\n", mods->real_mods );
+ fprintf( fs, "vmods: 0x%X\n", mods->vmods );
+}
+
+static void _XkbControlsDump( FILE * fs, XkbControlsPtr ctrls )
+{
+ int i;
+ char buf[1024];
+ fprintf( fs, "mk_dflt_btn: %d\n", ctrls->mk_dflt_btn );
+ fprintf( fs, "num_groups: %d\n", ctrls->num_groups );
+ fprintf( fs, "groups_wrap: %d\n", ctrls->groups_wrap );
+ fprintf( fs, "internal: \n" );
+ _XkbModsRecDump( fs, &ctrls->internal );
+ fprintf( fs, "ignore_lock: \n" );
+ _XkbModsRecDump( fs, &ctrls->ignore_lock );
+ fprintf( fs, "enabled_ctrls: 0x%X\n", ctrls->enabled_ctrls );
+ fprintf( fs, "repeat_delay: %d\n", ctrls->repeat_delay );
+ fprintf( fs, "repeat_interval: %d\n", ctrls->repeat_interval );
+ fprintf( fs, "slow_keys_delay: %d\n", ctrls->slow_keys_delay );
+ fprintf( fs, "debounce_delay: %d\n", ctrls->debounce_delay );
+ fprintf( fs, "mk_delay: %d\n", ctrls->mk_delay );
+ fprintf( fs, "mk_interval: %d\n", ctrls->mk_interval );
+ fprintf( fs, "mk_time_to_max: %d\n", ctrls->mk_time_to_max );
+ fprintf( fs, "mk_max_speed: %d\n", ctrls->mk_max_speed );
+ fprintf( fs, "mk_curve: %d\n", ctrls->mk_curve );
+ fprintf( fs, "ax_options: %d\n", ctrls->ax_options );
+ fprintf( fs, "ax_timeout: %d\n", ctrls->ax_timeout );
+ fprintf( fs, "axt_opts_mask: 0x%X\n", ctrls->axt_opts_mask );
+ fprintf( fs, "axt_opts_values: 0x%X\n", ctrls->axt_opts_values );
+ fprintf( fs, "axt_ctrls_mask: 0x%X\n", ctrls->axt_ctrls_mask );
+ fprintf( fs, "axt_ctrls_values: 0x%X\n", ctrls->axt_ctrls_values );
+ fprintf( fs, "axt_ctrls_values: 0x%X\n", ctrls->axt_ctrls_values );
+ fprintf( fs, "per_key_repeat:\n" );
+ buf[0] = 0;
+ for( i = 0; i < XkbPerKeyBitArraySize; i++ )
+ {
+ char b[5];
+ snprintf( b, sizeof( b ), "%d ", ctrls->per_key_repeat[i] );
+ strcat( buf, b );
+ }
+ fprintf( fs, " %s\n", buf );
+}
+
+static const char *actionTypeNames[] = {
+ "XkbSA_NoAction",
+ "XkbSA_SetMods",
+ "XkbSA_LatchMods",
+ "XkbSA_LockMods",
+ "XkbSA_SetGroup",
+ "XkbSA_LatchGroup",
+ "XkbSA_LockGroup",
+ "XkbSA_MovePtr",
+ "XkbSA_PtrBtn",
+ "XkbSA_LockPtrBtn",
+ "XkbSA_SetPtrDflt",
+ "XkbSA_ISOLock",
+ "XkbSA_Terminate",
+ "XkbSA_SwitchScreen",
+ "XkbSA_SetControls",
+ "XkbSA_LockControls",
+ "XkbSA_ActionMessage",
+ "XkbSA_RedirectKey",
+ "XkbSA_DeviceBtn",
+ "XkbSA_LockDeviceBtn",
+ "XkbSA_DeviceValuator"
+};
+
+static void _XkbActionDump( FILE * fs, int level, XkbAction * act )
+{
+ XkbGroupAction *ga;
+ fprintf( fs, "%*stype: %d(%s)\n", level, "", act->type,
+ actionTypeNames[act->type] );
+ switch ( act->type )
+ {
+ case XkbSA_SetGroup:
+ case XkbSA_LatchGroup:
+ case XkbSA_LockGroup:
+ ga = ( XkbGroupAction * ) act;
+ fprintf( fs, "%*sXkbGroupAction: \n", level, "" );
+ fprintf( fs, "%*sflags: %d\n", level, "", ga->flags );
+ fprintf( fs, "%*sgroup_XXX: %d\n", level, "", ga->group_XXX );
+ break;
+ }
+}
+
+static void _XkbBehaviorDump( FILE * fs, int level, XkbBehavior * b )
+{
+ fprintf( fs, "%*stype: %d\n", level, "", b->type );
+ fprintf( fs, "%*sdata: %d\n", level, "", b->data );
+}
+
+static void _XkbServerMapDump( FILE * fs, int level, XkbServerMapPtr server,
+ XkbDescPtr kbd )
+{
+ int i;
+ XkbAction *pa = server->acts;
+ XkbBehavior *pb = server->behaviors;
+ fprintf( fs, "%*snum_acts: %d\n", level, "", server->num_acts );
+ fprintf( fs, "%*ssize_acts: %d\n", level, "", server->size_acts );
+ if( server->acts != NULL )
+ {
+ for( i = 0; i < server->num_acts; i++ )
+ {
+ fprintf( fs, "%*sacts[%d]:\n", level, "", i );
+ _XkbActionDump( fs, level + 2, pa++ );
+ }
+ } else
+ fprintf( fs, "%*sNO acts\n", level, "" );
+
+ if( server->key_acts != NULL )
+ {
+ for( i = 0; i <= kbd->max_key_code; i++ )
+ {
+ fprintf( fs, "%*skey_acts[%d]: offset %d, total %d\n", level, "", i,
+ server->key_acts[i], XkbKeyNumSyms(kbd,i) );
+ }
+ } else
+ fprintf( fs, "%*sNO key_acts\n", level, "" );
+
+ for( i = 0; i < XkbNumVirtualMods; i++ )
+ {
+ fprintf( fs, "%*svmod[%d]: %X\n", level, "", i, server->vmods[i] );
+ }
+
+ if( server->behaviors != NULL )
+ {
+ for( i = 0; i <= kbd->max_key_code; i++ )
+ {
+ fprintf( fs, "%*sbehaviors[%d]:\n", level, "", i );
+ _XkbBehaviorDump( fs, level + 2, pb++ );
+ }
+ } else
+ fprintf( fs, "%*sNO behaviors\n", level, "" );
+
+ if( server->explicit != NULL )
+ {
+ for( i = 0; i <= kbd->max_key_code; i++ )
+ {
+ fprintf( fs, "%*sexplicit[%d]: %d\n", level, "", i,
+ server->explicit[i] );
+ }
+ } else
+ fprintf( fs, "%*sNO explicit\n", level, "" );
+
+ if( server->vmodmap != NULL )
+ {
+ for( i = 0; i <= kbd->max_key_code; i++ )
+ {
+ fprintf( fs, "%*svmodmap[%d]: %d\n", level, "", i, server->vmodmap[i] );
+ }
+ } else
+ fprintf( fs, "%*sNO vmodmap\n", level, "" );
+}
+
+static void _XkbKeyTypeDump( FILE * fs, int level, XkbKeyTypePtr type )
+{
+ char *z = type->name == None ? NULL : XGetAtomName( _xklDpy, type->name );
+ fprintf( fs, "%*sname: 0x%X(%s)\n", level, "", type->name, z );
+ if( z != NULL )
+ XFree( z );
+}
+
+static void _XkbSymMapDump( FILE * fs, int level, XkbSymMapPtr ksm )
+{
+ int i;
+ fprintf( fs, "%*skt_index: ", level, "" );
+ for( i = 0; i < XkbNumKbdGroups; i++ )
+ {
+ fprintf( fs, "%d ", ksm->kt_index[i] );
+ }
+ fprintf( fs, "\n%*sgroup_info: %d\n", level, "", ksm->group_info );
+ fprintf( fs, "%*swidth: %d\n", level, "", ksm->width );
+ fprintf( fs, "%*soffset: %d\n", level, "", ksm->offset );
+}
+
+static void _XkbClientMapDump( FILE * fs, int level, XkbClientMapPtr map,
+ XkbDescPtr kbd )
+{
+ int i;
+ fprintf( fs, "%*ssize_types: %d\n", level, "", map->size_types );
+ fprintf( fs, "%*snum_types: %d\n", level, "", map->num_types );
+ if( map->types != NULL )
+ {
+ XkbKeyTypePtr type = map->types;
+ for( i = 0; i < map->num_types; i++ )
+ {
+ fprintf( fs, "%*stypes[%d]:\n", level, "", i );
+ _XkbKeyTypeDump( fs, level + 2, type++ );
+ }
+ } else
+ fprintf( fs, "%*sNO types\n", level, "" );
+
+ fprintf( fs, "%*ssize_syms: %d\n", level, "", map->size_syms );
+ fprintf( fs, "%*snum_syms: %d\n", level, "", map->num_syms );
+ if( map->syms != NULL )
+ {
+ for( i = 0; i < map->num_syms; i++ )
+ fprintf( fs, "%*ssyms[%d]:0x%lX(%s)\n", level, "", i, map->syms[i],
+ XKeysymToString( map->syms[i] ) );
+ } else
+ fprintf( fs, "%*sNO syms\n", level, "" );
+ if( map->key_sym_map != NULL )
+ {
+ XkbSymMapPtr ksm = map->key_sym_map;
+ for( i = 0; i <= kbd->max_key_code; i++ )
+ {
+ fprintf( fs, "%*skey_sym_map[%d]:\n", level, "", i );
+ _XkbSymMapDump( fs, level + 2, ksm++ );
+ }
+ } else
+ fprintf( fs, "%*sNO key_sym_map\n", level, "" );
+}
+
+void _XkbDescDump( FILE * fs, int level, XkbDescPtr kbd )
+{
+ fprintf( fs, "%*sflags: 0x%X\n", level, "", kbd->flags );
+ fprintf( fs, "%*sdevice_spec: %d\n", level, "", kbd->device_spec );
+ fprintf( fs, "%*smin_key_code: %d\n", level, "", kbd->min_key_code );
+ fprintf( fs, "%*smax_key_code: %d\n", level, "", kbd->max_key_code );
+#if 0
+ if( kbd->ctrls != NULL )
+ {
+ fprintf( fs, "ctrls:\n" );
+ _XkbControlsDump( fs, kbd->ctrls );
+ } else
+ fprintf( fs, "NO server\n" );
+#endif
+ if( kbd->server != NULL )
+ {
+ fprintf( fs, "%*sserver:\n", level, "" );
+ _XkbServerMapDump( fs, level + 2, kbd->server, kbd );
+ } else
+ fprintf( fs, "%*sNO server\n", level, "" );
+
+ if( kbd->map != NULL )
+ {
+ fprintf( fs, "%*smap:\n", level, "" );
+ _XkbClientMapDump( fs, level + 2, kbd->map, kbd );
+ } else
+ fprintf( fs, "%*sNO map\n", level, "" );
+}
+
+void XklDumpXkbDesc( const char *filename, XkbDescPtr kbd )
+{
+ FILE *fs = fopen( filename, "w+" );
+ if( fs != NULL )
+ {
+ _XkbDescDump( fs, 0, kbd == NULL ? _xklXkb : kbd );
+ fclose( fs );
+ }
+
+}
diff --git a/libxklavier/xklavier_evt.c b/libxklavier/xklavier_evt.c
new file mode 100644
index 0000000..8ae9944
--- /dev/null
+++ b/libxklavier/xklavier_evt.c
@@ -0,0 +1,473 @@
+#include <time.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xlibint.h>
+
+#include "xklavier_private.h"
+
+int XklFilterEvents( XEvent * xev )
+{
+ XAnyEvent *pe = ( XAnyEvent * ) xev;
+ XklDebug( 400, "**> Filtering event %d of type %d from window %d\n",
+ pe->serial, pe->type, pe->window );
+ if( xev->type == _xklXkbEventType )
+ {
+ _XklXkbEvHandler( ( XkbEvent * ) xev );
+ } else
+ switch ( xev->type )
+ { /* core events */
+ case FocusIn:
+ _XklFocusInEvHandler( &xev->xfocus );
+ break;
+ case FocusOut:
+ _XklFocusOutEvHandler( &xev->xfocus );
+ break;
+ case PropertyNotify:
+ _XklPropertyEvHandler( &xev->xproperty );
+ break;
+ case CreateNotify:
+ _XklCreateEvHandler( &xev->xcreatewindow );
+ break;
+ case DestroyNotify:
+ XklDebug( 150, "Window " WINID_FORMAT " destroyed\n",
+ xev->xdestroywindow.window );
+ break;
+ case UnmapNotify:
+ XklDebug( 200, "UnmapNotify\n" );
+ break;
+ case MapNotify:
+ XklDebug( 200, "MapNotify\n" );
+ break;
+ case MappingNotify:
+ XklDebug( 200, "MappingNotify\n" );
+ _XklFreeAllInfo( );
+ _XklLoadAllInfo( );
+ break;
+ case GravityNotify:
+ XklDebug( 200, "GravityNotify\n" );
+ break;
+ case ReparentNotify:
+ XklDebug( 200, "ReparentNotify\n" );
+ break; /* Ignore these events */
+ default:
+ {
+ const char *name = _XklGetEventName( xev->type );
+ XklDebug( 200, "Unknown event %d [%s]\n", xev->type,
+ ( name == NULL ? "??" : name ) );
+ return 1;
+ }
+ }
+ XklDebug( 400, "Filtered event %d of type %d from window %d **>\n",
+ pe->serial, pe->type, pe->window );
+ return 0;
+}
+
+/**
+ * Some common functionality for Xkb handler
+ */
+void _XklStdXkbHandler( int grp, XklStateChange changeType, unsigned inds,
+ Bool setInds )
+{
+ Window focused, focusedApp;
+ XklState oldState;
+ int revert;
+ Bool haveState;
+ Bool setGroup = changeType == GROUP_CHANGED;
+
+ XGetInputFocus( _xklDpy, &focused, &revert );
+
+ if( ( focused == None ) || ( focused == PointerRoot ) )
+ {
+ XklDebug( 160, "Something with focus: " WINID_FORMAT "\n", focused );
+ return;
+ }
+
+ if( !_XklGetAppWindow( focused, &focusedApp ) )
+ focusedApp = _xklCurClient; //what else can I do
+
+ XklDebug( 150, "Focused window: " WINID_FORMAT ", '%s'\n", focusedApp,
+ _XklGetDebugWindowTitle( focusedApp ) );
+ XklDebug( 150, "CurClient: " WINID_FORMAT ", '%s'\n", _xklCurClient,
+ _XklGetDebugWindowTitle( _xklCurClient ) );
+
+ if( focusedApp != _xklCurClient )
+ {
+ _xklCurState.group = grp;
+ _xklCurState.indicators = inds;
+
+ _XklAddAppWindow( focusedApp, ( Window ) NULL, False, &_xklCurState );
+ _xklCurClient = focusedApp;
+ XklDebug( 160, "CurClient:changed to " WINID_FORMAT ", '%s'\n",
+ _xklCurClient, _XklGetDebugWindowTitle( _xklCurClient ) );
+ }
+ // if the window already has this this state - we are just restoring it!
+ // (see the second parameter of stateCallback
+ haveState = _XklGetAppState( _xklCurClient, &oldState );
+
+ if( setGroup || haveState )
+ {
+ _xklCurState.group = setGroup ? grp : oldState.group;
+ _xklCurState.indicators = setInds ? inds : oldState.indicators;
+ }
+
+ if( haveState )
+ _XklTryCallStateCallback( changeType, &oldState );
+
+ _XklSaveAppState( _xklCurClient, &_xklCurState );
+}
+
+/**
+ * XKB event handler
+ */
+void _XklXkbEvHandler( XkbEvent * kev )
+{
+ int i;
+ unsigned bit;
+ unsigned inds;
+
+ XklDebug( 150, "Xkb event detected\n" );
+
+ switch ( kev->any.xkb_type )
+ {
+ case XkbStateNotify:
+#define GROUP_CHANGE_MASK \
+ ( XkbGroupStateMask | XkbGroupBaseMask | XkbGroupLatchMask | XkbGroupLockMask )
+
+ XklDebug( 150,
+ "XkbStateNotify detected, changes: %X/(mask %X), new group %d\n",
+ kev->state.changed, GROUP_CHANGE_MASK,
+ kev->state.locked_group );
+
+ if( kev->state.changed & GROUP_CHANGE_MASK )
+ _XklStdXkbHandler( kev->state.locked_group, GROUP_CHANGED, 0, False );
+ else
+ XklDebug( 200,
+ "This type of state notification is not regarding groups\n" );
+
+ break;
+
+ case XkbIndicatorStateNotify:
+
+ XklDebug( 150, "XkbIndicatorStateNotify\n" );
+
+ inds = _xklCurState.indicators;
+
+ ForPhysIndicators( i, bit ) if( kev->indicators.changed & bit )
+ {
+ if( kev->indicators.state & bit )
+ inds |= bit;
+ else
+ inds &= ~bit;
+ }
+
+ _XklStdXkbHandler( 0, INDICATORS_CHANGED, inds, True );
+ break;
+
+ case XkbIndicatorMapNotify:
+ XklDebug( 150, "XkbIndicatorMapNotify\n" );
+ _XklFreeAllInfo( );
+ _XklLoadAllInfo( );
+ break;
+
+ case XkbControlsNotify:
+ XklDebug( 150, "XkbControlsNotify\n" );
+ _XklFreeAllInfo( );
+ _XklLoadAllInfo( );
+ break;
+
+ case XkbNamesNotify:
+ XklDebug( 150, "XkbNamesNotify\n" );
+ _XklFreeAllInfo( );
+ _XklLoadAllInfo( );
+ break;
+
+ case XkbNewKeyboardNotify:
+ XklDebug( 150, "XkbNewKeyboardNotify\n" );
+ _XklFreeAllInfo( );
+ _XklLoadAllInfo( );
+ break;
+
+ default:
+ XklDebug( 150, "Unknown xkb event %d\n", kev->any.xkb_type );
+ break;
+ }
+}
+
+/**
+ * FocusIn handler
+ */
+void _XklFocusInEvHandler( XFocusChangeEvent * fev )
+{
+ Window win;
+ Window appWin;
+ XklState selectedWindowState;
+
+ win = fev->window;
+
+ switch ( fev->mode )
+ {
+ case NotifyNormal:
+ case NotifyWhileGrabbed:
+ break;
+ default:
+ XklDebug( 160,
+ "Window " WINID_FORMAT
+ " has got focus during special action %d\n", win, fev->mode );
+ return;
+ }
+
+ XklDebug( 150, "Window " WINID_FORMAT ", '%s' has got focus\n", win,
+ _XklGetDebugWindowTitle( win ) );
+
+ if( !_XklGetAppWindow( win, &appWin ) )
+ {
+ return;
+ }
+
+ XklDebug( 150, "Appwin " WINID_FORMAT ", '%s' has got focus\n", appWin,
+ _XklGetDebugWindowTitle( appWin ) );
+
+ if( XklGetState( appWin, &selectedWindowState ) )
+ {
+ if( _xklCurClient != appWin )
+ {
+ Bool transparent;
+ _xklCurClient = appWin;
+ XklDebug( 150, "CurClient:changed to " WINID_FORMAT ", '%s'\n",
+ _xklCurClient, _XklGetDebugWindowTitle( _xklCurClient ) );
+
+ transparent = _XklIsTransparentAppWindow( appWin );
+ if( transparent )
+ XklDebug( 150, "Entering transparent window\n" );
+ if( XklIsGroupPerApp( ) && !transparent )
+ {
+ // We skip restoration only if we return to the same app window
+ Bool doSkip = False;
+ if( _xklSkipOneRestore )
+ {
+ _xklSkipOneRestore = False;
+ if( appWin == _xklPrevAppWindow )
+ doSkip = True;
+ }
+
+ if( doSkip )
+ {
+ XklDebug( 150,
+ "Skipping one restore as requested - instead, saving the current group into the window state\n" );
+ _XklSaveAppState( appWin, &_xklCurState );
+ } else
+ {
+ if( _xklCurState.group != selectedWindowState.group )
+ {
+ XklDebug( 150,
+ "Restoring the group from %d to %d after gaining focus\n",
+ _xklCurState.group, selectedWindowState.group );
+ XklLockGroup( selectedWindowState.group );
+ } else
+ {
+ XklDebug( 150,
+ "Both old and new focused window have state %d so no point restoring it\n",
+ selectedWindowState.group );
+ _xklAllowSecondaryGroupOnce = False;
+ }
+ }
+
+ if( XklGetIndicatorsHandling( ) )
+ {
+ int i;
+ unsigned bit;
+
+ XklDebug( 150,
+ "Restoring the indicators from %X to %X after gaining focus\n",
+ _xklCurState.indicators, selectedWindowState.indicators );
+ ForPhysIndicators( i,
+ bit ) if( _xklXkb->names->indicators[i] != None )
+ {
+ Bool status;
+ status =
+ _XklSetIndicator( i,
+ ( selectedWindowState.indicators & bit ) !=
+ 0 );
+ XklDebug( 150, "Set indicator \"%s\"/%d to %d: %d\n",
+ _xklIndicatorNames[i], _xklXkb->names->indicators[i],
+ selectedWindowState.indicators & bit, status );
+ }
+ } else
+ XklDebug( 150,
+ "Not restoring the indicators %X after gaining focus: indicator handling is not enabled\n",
+ _xklCurState.indicators );
+ } else
+ XklDebug( 150,
+ "Not restoring the group %d after gaining focus: global layout (or transparent window)\n",
+ _xklCurState.group );
+ } else
+ XklDebug( 150, "Same app window - just do nothing\n" );
+ } else
+ {
+ XklDebug( 150, "But it does not have xklavier_state\n" );
+ if( _XklHasWmState( win ) )
+ {
+ XklDebug( 150, "But it does have wm_state so we'll add it\n" );
+ _xklCurClient = appWin;
+ XklDebug( 150, "CurClient:changed to " WINID_FORMAT ", '%s'\n",
+ _xklCurClient, _XklGetDebugWindowTitle( _xklCurClient ) );
+ _XklAddAppWindow( _xklCurClient, ( Window ) NULL, False,
+ &_xklCurState );
+ } else
+ XklDebug( 150, "And it does have wm_state either\n" );
+ }
+}
+
+/**
+ * FocusOut handler
+ */
+void _XklFocusOutEvHandler( XFocusChangeEvent * fev )
+{
+ if( fev->mode != NotifyNormal )
+ {
+ XklDebug( 200,
+ "Window " WINID_FORMAT
+ " has lost focus during special action %d\n", fev->window,
+ fev->mode );
+ return;
+ }
+
+ XklDebug( 160, "Window " WINID_FORMAT ", '%s' has lost focus\n",
+ fev->window, _XklGetDebugWindowTitle( fev->window ) );
+
+ if( XklIsTransparent( fev->window ) )
+ {
+
+ XklDebug( 150, "Leaving transparent window!\n" );
+/**
+ * If we are leaving the transparent window - we skip the restore operation.
+ * This is useful for secondary groups switching from the transparent control
+ * window.
+ */
+ _xklSkipOneRestore = True;
+ } else
+ {
+ Window p;
+ if( _XklGetAppWindow( fev->window, &p ) )
+ _xklPrevAppWindow = p;
+ }
+
+}
+
+/**
+ * PropertyChange handler
+ * Interested in WM_STATE property only
+ */
+void _XklPropertyEvHandler( XPropertyEvent * pev )
+{
+ if( 400 <= _xklDebugLevel )
+ {
+ char *atomName = XGetAtomName( _xklDpy, pev->atom );
+ if( atomName != NULL )
+ {
+ XklDebug( 400, "The property '%s' changed for " WINID_FORMAT "\n",
+ atomName, pev->window );
+ XFree( atomName );
+ } else
+ {
+ XklDebug( 200, "Some magic property changed for " WINID_FORMAT "\n",
+ pev->window );
+ }
+ }
+
+ if( pev->atom == _xklAtoms[WM_STATE] )
+ {
+ Bool hasXklState = XklGetState( pev->window, NULL );
+
+ if( pev->state == PropertyNewValue )
+ {
+ XklDebug( 160, "New value of WM_STATE on window " WINID_FORMAT "\n",
+ pev->window );
+ if( !hasXklState ) /* Is this event the first or not? */
+ {
+ _XklAddAppWindow( pev->window, ( Window ) NULL, False,
+ &_xklCurState );
+ }
+ } else
+ { /* ev->xproperty.state == PropertyDelete, either client or WM can remove it, ICCCM 4.1.3.1 */
+ XklDebug( 160, "Something (%d) happened to WM_STATE of window 0x%x\n",
+ pev->state, pev->window );
+ _XklSelectInputMerging( pev->window, PropertyChangeMask );
+ if( hasXklState )
+ {
+ XklDelState( pev->window );
+ }
+ }
+ } else
+ if( pev->atom == _xklAtoms[XKB_RF_NAMES_PROP_ATOM]
+ && pev->window == _xklRootWindow )
+ {
+ if( pev->state == PropertyNewValue )
+ {
+ XklDebug( 160, "New value of XKB_RF_NAMES_PROP_ATOM on root window\n" );
+ // If root window got new _XKB_RF_NAMES_PROP_ATOM -
+ // it most probably means new xkb is loaded by somebody
+ _XklFreeAllInfo( );
+ _XklLoadAllInfo( );
+ }
+ }
+}
+
+/**
+ * * CreateNotify handler. Just interested in properties and focus events...
+ * */
+void _XklCreateEvHandler( XCreateWindowEvent * cev )
+{
+ long newmask;
+ XklDebug( 200,
+ "Under-root window " WINID_FORMAT
+ "/%s (%d,%d,%d x %d) is created\n", cev->window,
+ _XklGetDebugWindowTitle( cev->window ), cev->x, cev->y,
+ cev->width, cev->height );
+
+ if( !cev->override_redirect )
+ {
+/* ICCCM 4.1.6: override-redirect is NOT private to
+* client (and must not be changed - am I right?)
+* We really need only PropertyChangeMask on this window but even in the case of
+* local server we can lose PropertyNotify events (the trip time for this CreateNotify
+* event + SelectInput request is not zero) and we definitely will (my system DO)
+* lose FocusIn/Out events after the following call of PropertyNotifyHandler.
+* So I just decided to purify this extra FocusChangeMask in the FocusIn/OutHandler. */
+ _XklSelectInputMerging( cev->window,
+ PropertyChangeMask | FocusChangeMask );
+
+ if( _XklHasWmState( cev->window ) )
+ {
+ XklDebug( 200,
+ "Just created window already has WM_STATE - so I'll add it" );
+ _XklAddAppWindow( cev->window, ( Window ) NULL, False, &_xklCurState );
+ }
+ }
+}
+
+/**
+ * Just error handler - sometimes we get BadWindow error for already gone
+ * windows, so we'll just ignore
+ */
+void _XklErrHandler( Display * dpy, XErrorEvent * evt )
+{
+ _xklLastErrorCode = evt->error_code;
+ switch ( _xklLastErrorCode )
+ {
+ case BadWindow:
+ case BadAccess:
+ {
+ // in most cases this means we are late:)
+ XklDebug( 200, "ERROR: %p, " WINID_FORMAT ", %d, %d, %d\n",
+ dpy,
+ ( unsigned long ) evt->resourceid,
+ ( int ) evt->error_code,
+ ( int ) evt->request_code, ( int ) evt->minor_code );
+ break;
+ }
+ default:
+ ( *_xklDefaultErrHandler ) ( dpy, evt );
+ }
+}
diff --git a/libxklavier/xklavier_private.h b/libxklavier/xklavier_private.h
new file mode 100644
index 0000000..7735f86
--- /dev/null
+++ b/libxklavier/xklavier_private.h
@@ -0,0 +1,139 @@
+#ifndef __XKLAVIER_PRIVATE_H__
+#define __XKLAVIER_PRIVATE_H__
+
+#include <stdio.h>
+
+#include <libxklavier/xklavier_config.h>
+
+#include <X11/extensions/XKBrules.h>
+
+extern void _XklGetRealState( XklState * curState_return );
+extern void _XklAddAppWindow( Window win, Window parent, Bool force,
+ XklState * initState );
+extern Bool _XklGetAppWindowBottomToTop( Window win, Window * appWin_return );
+extern Bool _XklGetAppWindow( Window win, Window * appWin_return );
+
+extern void _XklStdXkbHandler( int grp, XklStateChange changeType,
+ unsigned inds, Bool setInds );
+extern void _XklXkbEvHandler( XkbEvent * kev );
+extern void _XklFocusInEvHandler( XFocusChangeEvent * fev );
+extern void _XklFocusOutEvHandler( XFocusChangeEvent * fev );
+extern void _XklPropertyEvHandler( XPropertyEvent * rev );
+extern void _XklCreateEvHandler( XCreateWindowEvent * cev );
+
+extern void _XklErrHandler( Display * dpy, XErrorEvent * evt );
+
+extern Window _XklGetRegisteredParent( Window win );
+extern Bool _XklLoadAllInfo( void );
+extern void _XklFreeAllInfo( void );
+extern Bool _XklLoadWindowTree( void );
+extern Bool _XklLoadSubtree( Window window, int level, XklState * initState );
+
+extern Bool _XklHasWmState( Window win );
+
+extern Bool _XklGetAppState( Window appWin, XklState * state_return );
+extern void _XklDelAppState( Window appWin );
+extern void _XklSaveAppState( Window appWin, XklState * state );
+
+extern void _XklSelectInputMerging( Window win, long mask );
+
+extern char *_XklGetDebugWindowTitle( Window win );
+
+extern Status _XklStatusQueryTree( Display * display,
+ Window w,
+ Window * root_return,
+ Window * parent_return,
+ Window ** children_return,
+ signed int *nchildren_return );
+
+extern Bool _XklSetIndicator( int indicatorNum, Bool set );
+
+extern void _XklTryCallStateCallback( XklStateChange changeType,
+ XklState * oldState );
+
+extern void _XklI18NInit( );
+
+extern char *_XklLocaleFromUtf8( const char *utf8string );
+
+extern int _XklGetLanguagePriority( const char *language );
+
+extern char *_XklConfigRecMergeByComma( const char **array,
+ const int arrayLength );
+
+extern char *_XklConfigRecMergeLayouts( const XklConfigRecPtr data );
+
+extern char *_XklConfigRecMergeVariants( const XklConfigRecPtr data );
+
+extern char *_XklConfigRecMergeOptions( const XklConfigRecPtr data );
+
+extern void _XklConfigRecSplitByComma( char ***array,
+ int *arraySize, const char *merged );
+
+extern void _XklConfigRecSplitLayouts( XklConfigRecPtr data,
+ const char *merged );
+
+extern void _XklConfigRecSplitVariants( XklConfigRecPtr data,
+ const char *merged );
+
+extern void _XklConfigRecSplitOptions( XklConfigRecPtr data,
+ const char *merged );
+
+extern void XklDumpXkbDesc( const char *filename, XkbDescPtr kbd );
+
+extern const char *_XklGetEventName( int type );
+
+extern Bool _XklIsTransparentAppWindow( Window appWin );
+
+extern Display *_xklDpy;
+
+extern Bool _xklXkbExtPresent;
+
+extern Window _xklRootWindow;
+
+extern XkbDescPtr _xklXkb;
+
+extern XklState _xklCurState;
+
+extern Window _xklCurClient;
+
+extern Status _xklLastErrorCode;
+
+extern const char *_xklLastErrorMsg;
+
+extern XErrorHandler _xklDefaultErrHandler;
+
+extern char *_xklIndicatorNames[];
+
+#define ForPhysIndicators( i, bit ) \
+ for ( i=0, bit=1; i<XkbNumIndicators; i++, bit<<=1 ) \
+ if ( _xklXkb->indicators->phys_indicators & bit )
+
+extern int _xklXkbEventType, _xklXkbError;
+
+#define WM_NAME 0
+#define WM_STATE 1
+#define XKLAVIER_STATE 2
+#define XKLAVIER_TRANSPARENT 3
+#define XKB_RF_NAMES_PROP_ATOM 4
+#define XKB_RF_NAMES_PROP_ATOM_BACKUP 5
+#define TOTAL_ATOMS 6
+
+extern Atom _xklAtoms[];
+
+extern XkbRF_VarDefsRec _xklVarDefs;
+
+extern Bool _xklAllowSecondaryGroupOnce;
+
+extern int _xklDefaultGroup;
+
+extern Bool _xklSkipOneRestore;
+
+extern int _xklSecondaryGroupMask;
+
+extern int _xklDebugLevel;
+
+extern Window _xklPrevAppWindow;
+
+#define WINID_FORMAT "%lx"
+
+#endif
diff --git a/libxklavier/xklavier_props.c b/libxklavier/xklavier_props.c
new file mode 100644
index 0000000..79b305a
--- /dev/null
+++ b/libxklavier/xklavier_props.c
@@ -0,0 +1,359 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/XKBfile.h>
+#include <X11/extensions/XKBrules.h>
+#include <X11/extensions/XKM.h>
+
+#include <libxml/xpath.h>
+
+#include "config.h"
+
+#include "xklavier.h"
+#include "xklavier_config.h"
+#include "xklavier_private.h"
+
+void XklConfigRecInit( XklConfigRecPtr data )
+{
+ // clear the structure VarDefsPtr...
+ memset( ( void * ) data, 0, sizeof( XklConfigRec ) );
+}
+
+void XklConfigRecDestroy( XklConfigRecPtr data )
+{
+ int i;
+ char **p;
+
+ if( data->model != NULL )
+ free( data->model );
+
+ if( ( p = data->layouts ) != NULL )
+ {
+ for( i = data->numLayouts; --i >= 0; )
+ free( *p++ );
+ free( data->layouts );
+ }
+
+ if( ( p = data->variants ) != NULL )
+ {
+ for( i = data->numVariants; --i >= 0; )
+ free( *p++ );
+ free( data->variants );
+ }
+
+ if( ( p = data->options ) != NULL )
+ {
+ for( i = data->numOptions; --i >= 0; )
+ free( *p++ );
+ free( data->options );
+ }
+}
+
+void XklConfigRecReset( XklConfigRecPtr data )
+{
+ XklConfigRecDestroy( data );
+ XklConfigRecInit( data );
+}
+
+Bool XklConfigGetFromServer( XklConfigRecPtr data )
+{
+ char *rulesFile = NULL;
+ Bool rv =
+ XklGetNamesProp( _xklAtoms[XKB_RF_NAMES_PROP_ATOM], &rulesFile, data );
+ if( rulesFile != NULL )
+ free( rulesFile );
+
+ return rv;
+}
+
+Bool XklConfigGetFromBackup( XklConfigRecPtr data )
+{
+ char *rulesFile = NULL;
+ Bool rv =
+ XklGetNamesProp( _xklAtoms[XKB_RF_NAMES_PROP_ATOM_BACKUP], &rulesFile,
+ data );
+ if( rulesFile != NULL )
+ free( rulesFile );
+
+ return rv;
+}
+
+// taken from XFree86 maprules.c
+Bool XklGetNamesProp( Atom rulesAtom,
+ char **rulesFileOut, XklConfigRecPtr data )
+{
+ Atom realPropType;
+ int fmt;
+ unsigned long nitems, extraBytes;
+ char *propData, *out;
+ Status rtrn;
+
+ // no such atom!
+ if( rulesAtom == None ) /* property cannot exist */
+ {
+ _xklLastErrorMsg = "Could not find the atom";
+ return False;
+ }
+
+ rtrn =
+ XGetWindowProperty( _xklDpy, _xklRootWindow, rulesAtom, 0L,
+ _XKB_RF_NAMES_PROP_MAXLEN, False, XA_STRING,
+ &realPropType, &fmt, &nitems, &extraBytes,
+ ( unsigned char ** ) &propData );
+ // property not found!
+ if( rtrn != Success )
+ {
+ _xklLastErrorMsg = "Could not get the property";
+ return False;
+ }
+ // set rules file to ""
+ if( rulesFileOut )
+ *rulesFileOut = NULL;
+
+ // has to be array of strings
+ if( ( extraBytes > 0 ) || ( realPropType != XA_STRING ) || ( fmt != 8 ) )
+ {
+ if( propData )
+ XFree( propData );
+ _xklLastErrorMsg = "Wrong property format";
+ return False;
+ }
+ // rules file
+ out = propData;
+ if( out && ( *out ) && rulesFileOut )
+ *rulesFileOut = strdup( out );
+ out += strlen( out ) + 1;
+
+ if( ( out - propData ) < nitems )
+ {
+ if( *out )
+ data->model = strdup( out );
+ out += strlen( out ) + 1;
+ }
+
+ if( ( out - propData ) < nitems )
+ {
+ _XklConfigRecSplitLayouts( data, out );
+ out += strlen( out ) + 1;
+ }
+
+ if( ( out - propData ) < nitems )
+ {
+ int i;
+ char **theLayout, **theVariant;
+ _XklConfigRecSplitVariants( data, out );
+ /*
+ Now have to ensure that number of variants matches the number of layouts
+ The 'remainder' is filled with NULLs (not ""s!)
+ */
+ if( data->numVariants < data->numLayouts )
+ {
+ data->variants =
+ realloc( data->variants, data->numLayouts * sizeof( char * ) );
+ memset( data->variants + data->numVariants, 0,
+ ( data->numLayouts - data->numVariants ) * sizeof( char * ) );
+ data->numVariants = data->numLayouts;
+ }
+ // take variants from layouts like ru(winkeys)
+ theLayout = data->layouts;
+ theVariant = data->variants;
+ for( i = data->numLayouts; --i >= 0; theLayout++, theVariant++ )
+ {
+ if( *theLayout != NULL )
+ {
+ char *varstart = strchr( *theLayout, '(' );
+ if( varstart != NULL )
+ {
+ char *varend = strchr( varstart, ')' );
+ if( varend != NULL )
+ {
+ int varlen = varend - varstart;
+ int laylen = varstart - *theLayout;
+ // I am not sure - but I assume variants in layout have priority
+ char *var = *theVariant = ( *theVariant != NULL ) ?
+ realloc( *theVariant, varlen ) : malloc( varlen );
+ memcpy( var, varstart + 1, --varlen );
+ var[varlen] = '\0';
+ realloc( *theLayout, laylen + 1 );
+ ( *theLayout )[laylen] = '\0';
+ }
+ }
+ }
+ }
+ out += strlen( out ) + 1;
+ }
+
+ if( ( out - propData ) < nitems )
+ {
+ _XklConfigRecSplitOptions( data, out );
+// out += strlen( out ) + 1;
+ }
+ XFree( propData );
+ return True;
+}
+
+// taken from XFree86 maprules.c
+Bool XklSetNamesProp( Atom rulesAtom,
+ char *rulesFile, const XklConfigRecPtr data )
+{
+ int len, i, rv;
+ char *pval;
+ char *next;
+ char *allLayouts = _XklConfigRecMergeLayouts( data );
+ char *allVariants = _XklConfigRecMergeVariants( data );
+ char *allOptions = _XklConfigRecMergeOptions( data );
+
+ len = ( rulesFile ? strlen( rulesFile ) : 0 );
+ len += ( data->model ? strlen( data->model ) : 0 );
+ len += ( allLayouts ? strlen( allLayouts ) : 0 );
+ len += ( allVariants ? strlen( allVariants ) : 0 );
+ len += ( allOptions ? strlen( allOptions ) : 0 );
+ if( len < 1 )
+ return True;
+
+ len += 5; /* trailing NULs */
+
+ pval = next = ( char * ) malloc( len + 1 );
+ if( !pval )
+ {
+ _xklLastErrorMsg = "Could not allocate buffer";
+ return False;
+ }
+ if( rulesFile )
+ {
+ strcpy( next, rulesFile );
+ next += strlen( rulesFile );
+ }
+ *next++ = '\0';
+ if( data->model )
+ {
+ strcpy( next, data->model );
+ next += strlen( data->model );
+ }
+ *next++ = '\0';
+ if( data->layouts )
+ {
+ strcpy( next, allLayouts );
+ next += strlen( allLayouts );
+ }
+ *next++ = '\0';
+ if( data->variants )
+ {
+ strcpy( next, allVariants );
+ next += strlen( allVariants );
+ }
+ *next++ = '\0';
+ if( data->options )
+ {
+ strcpy( next, allOptions );
+ next += strlen( allOptions );
+ }
+ *next++ = '\0';
+ if( ( next - pval ) != len )
+ {
+ XklDebug( 150, "Illegal final position: %d/%d\n", ( next - pval ), len );
+ if( allOptions != NULL )
+ free( allOptions );
+ free( pval );
+ _xklLastErrorMsg = "Internal property parsing error";
+ return False;
+ }
+
+ rv = XChangeProperty( _xklDpy, _xklRootWindow, rulesAtom, XA_STRING, 8,
+ PropModeReplace, ( unsigned char * ) pval, len );
+ XSync( _xklDpy, False );
+#if 0
+ for( i = len - 1; --i >= 0; )
+ if( pval[i] == '\0' )
+ pval[i] = '?';
+ XklDebug( 150, "Stored [%s] of length %d to [%s] of %X: %d\n", pval, len,
+ propName, _xklRootWindow, rv );
+#endif
+ if( allOptions != NULL )
+ free( allOptions );
+ free( pval );
+ return True;
+}
+
+Bool XklBackupNamesProp( )
+{
+ char *rf;
+ XklConfigRec data;
+ Bool rv = True;
+
+ XklConfigRecInit( &data );
+ if( XklGetNamesProp
+ ( _xklAtoms[XKB_RF_NAMES_PROP_ATOM_BACKUP], &rf, &data ) )
+ {
+ XklConfigRecDestroy( &data );
+ if( rf != NULL )
+ free( rf );
+ return True;
+ }
+ // "backup" property is not defined
+ XklConfigRecReset( &data );
+ if( XklGetNamesProp( _xklAtoms[XKB_RF_NAMES_PROP_ATOM], &rf, &data ) )
+ {
+#if 0
+ int i;
+ XklDebug( 150, "Original model: [%s]\n", data.model );
+
+ XklDebug( 150, "Original layouts(%d):\n", data.numLayouts );
+ for( i = data.numLayouts; --i >= 0; )
+ XklDebug( 150, "%d: [%s]\n", i, data.layouts[i] );
+
+ XklDebug( 150, "Original variants(%d):\n", data.numVariants );
+ for( i = data.numVariants; --i >= 0; )
+ XklDebug( 150, "%d: [%s]\n", i, data.variants[i] );
+
+ XklDebug( 150, "Original options(%d):\n", data.numOptions );
+ for( i = data.numOptions; --i >= 0; )
+ XklDebug( 150, "%d: [%s]\n", i, data.options[i] );
+#endif
+ if( !XklSetNamesProp( _xklAtoms[XKB_RF_NAMES_PROP_ATOM_BACKUP], rf, &data ) )
+ {
+ XklDebug( 150, "Could not backup the configuration" );
+ rv = False;
+ }
+ if( rf != NULL )
+ free( rf );
+ } else
+ {
+ XklDebug( 150, "Could not get the configuration for backup" );
+ rv = False;
+ }
+ XklConfigRecDestroy( &data );
+
+ return rv;
+}
+
+Bool XklRestoreNamesProp( )
+{
+ char *rf;
+ XklConfigRec data;
+ Bool rv = True;
+
+ XklConfigRecInit( &data );
+ if( !XklGetNamesProp( _xklAtoms[XKB_RF_NAMES_PROP_ATOM_BACKUP], &rf, &data ) )
+ {
+ XklConfigRecDestroy( &data );
+ return False;
+ }
+
+ if( rf != NULL )
+ free( rf );
+
+ if( !XklSetNamesProp( _xklAtoms[XKB_RF_NAMES_PROP_ATOM], rf, &data ) )
+ {
+ XklDebug( 150, "Could not backup the configuration" );
+ rv = False;
+ }
+ XklConfigRecDestroy( &data );
+
+ return rv;
+}
diff --git a/libxklavier/xklavier_util.c b/libxklavier/xklavier_util.c
new file mode 100644
index 0000000..37b9595
--- /dev/null
+++ b/libxklavier/xklavier_util.c
@@ -0,0 +1,441 @@
+#include <time.h>
+
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/Xlibint.h>
+
+#include "xklavier_private.h"
+
+XklState *XklGetCurrentState( )
+{
+ return &_xklCurState;
+}
+
+const char *XklGetLastError( )
+{
+ return _xklLastErrorMsg;
+}
+
+char *XklGetWindowTitle( Window w )
+{
+ Atom type_ret;
+ int format_ret;
+ unsigned long nitems, rest;
+ unsigned char *prop;
+
+ if( Success == XGetWindowProperty( _xklDpy, w, _xklAtoms[WM_NAME], 0L,
+ -1L, False, XA_STRING, &type_ret,
+ &format_ret, &nitems, &rest, &prop ) )
+ return prop;
+ else
+ return NULL;
+}
+
+void XklLockGroup( int group )
+{
+ XklDebug( 100, "Posted request for change the group to %d ##\n", group );
+ XkbLockGroup( _xklDpy, XkbUseCoreKbd, group );
+ XSync( _xklDpy, False );
+}
+
+Bool XklIsSameApp( Window win1, Window win2 )
+{
+ Window app1, app2;
+ return _XklGetAppWindow( win1, &app1 ) &&
+ _XklGetAppWindow( win2, &app2 ) && app1 == app2;
+}
+
+Bool XklGetState( Window win, XklState * state_return )
+{
+ Window appWin;
+
+ if( !_XklGetAppWindow( win, &appWin ) )
+ {
+ if( state_return != NULL )
+ state_return->group = -1;
+ return False;
+ }
+
+ return _XklGetAppState( appWin, state_return );
+}
+
+void XklDelState( Window win )
+{
+ Window appWin;
+
+ if( _XklGetAppWindow( win, &appWin ) )
+ _XklDelAppState( appWin );
+}
+
+void XklSaveState( Window win, XklState * state )
+{
+ Window appWin;
+
+ if( _XklGetAppWindow( win, &appWin ) )
+ _XklSaveAppState( appWin, state );
+}
+
+/**
+ * Updates current internal state from X state
+ */
+void _XklGetRealState( XklState * curState_return )
+{
+ XkbStateRec state;
+
+ curState_return->group = 0;
+ if( Success == XkbGetState( _xklDpy, XkbUseCoreKbd, &state ) )
+ curState_return->group = state.locked_group;
+
+ if( Success ==
+ XkbGetIndicatorState( _xklDpy, XkbUseCoreKbd,
+ &curState_return->indicators ) )
+ curState_return->indicators &= _xklXkb->indicators->phys_indicators;
+ else
+ curState_return->indicators = 0;
+
+}
+
+/**
+ * Prepares the name of window suitable for debugging (32characters long).
+ */
+char *_XklGetDebugWindowTitle( Window win )
+{
+ static char sname[33];
+ char *name;
+ strcpy( sname, "NULL" );
+ if( win != ( Window ) NULL )
+ {
+ name = XklGetWindowTitle( win );
+ if( name != NULL )
+ {
+ snprintf( sname, sizeof( sname ), "%.32s", name );
+ XFree( name );
+ }
+ }
+ return sname;
+}
+
+Window XklGetCurrentWindow( )
+{
+ return _xklCurClient;
+}
+
+/**
+ * Loads subtree.
+ * All the windows with WM_STATE are added.
+ * All the windows within level 0 are listened for focus and property
+ */
+Bool _XklLoadSubtree( Window window, int level, XklState * initState )
+{
+ Window rwin = ( Window ) NULL,
+ parent = ( Window ) NULL, *children = NULL, *child;
+ int num = 0;
+ Bool retval = True;
+
+ _xklLastErrorCode =
+ _XklStatusQueryTree( _xklDpy, window, &rwin, &parent, &children, &num );
+
+ if( _xklLastErrorCode != Success )
+ {
+ return False;
+ }
+
+ child = children;
+ while( num )
+ {
+ XklDebug( 150, "Looking at child " WINID_FORMAT " '%s'\n", *child,
+ _XklGetDebugWindowTitle( *child ) );
+ if( _XklHasWmState( *child ) )
+ {
+ XklDebug( 150, "It has WM_STATE so we'll add it\n" );
+ _XklAddAppWindow( *child, window, True, initState );
+ } else
+ {
+ XklDebug( 150, "It does not have have WM_STATE so we'll not add it\n" );
+
+ if( level == 0 )
+ {
+ XklDebug( 150, "But we are at level 0 so we'll spy on it\n" );
+ _XklSelectInputMerging( *child,
+ FocusChangeMask | PropertyChangeMask );
+ } else
+ XklDebug( 150, "And we are at level %d so we'll not spy on it\n",
+ level );
+
+ retval = _XklLoadSubtree( *child, level + 1, initState );
+ }
+
+ child++;
+ num--;
+ }
+
+ if( children != NULL )
+ XFree( children );
+
+ return retval;
+}
+
+/**
+ * Checks whether given window has WM_STATE property (i.e. "App window").
+ */
+Bool _XklHasWmState( Window win )
+{ /* ICCCM 4.1.3.1 */
+ Atom type = None;
+ int format;
+ unsigned long nitems;
+ unsigned long after;
+ unsigned char *data = NULL; /* Helps in the case of BadWindow error */
+
+ XGetWindowProperty( _xklDpy, win, _xklAtoms[WM_STATE], 0, 0, False,
+ _xklAtoms[WM_STATE], &type, &format, &nitems, &after,
+ &data );
+ if( data != NULL )
+ XFree( data ); /* To avoid an one-byte memory leak because after successfull return
+ * data array always contains at least one nul byte (NULL-equivalent) */
+ return type != None;
+}
+
+/**
+ * Finds out the official parent window (accortind to XQueryTree)
+ */
+Window _XklGetRegisteredParent( Window win )
+{
+ Window parent = ( Window ) NULL, rw = ( Window ) NULL, *children = NULL;
+ unsigned nchildren = 0;
+
+ _xklLastErrorCode =
+ _XklStatusQueryTree( _xklDpy, win, &rw, &parent, &children, &nchildren );
+
+ if( children != NULL )
+ XFree( children );
+
+ return _xklLastErrorCode == Success ? parent : ( Window ) NULL;
+}
+
+/**
+ * Make sure about the result. Origial XQueryTree is pretty stupid beast:)
+ */
+Status _XklStatusQueryTree( Display * display,
+ Window w,
+ Window * root_return,
+ Window * parent_return,
+ Window ** children_return,
+ signed int *nchildren_return )
+{
+ Bool result;
+
+ result = ( Bool ) XQueryTree( display,
+ w,
+ root_return,
+ parent_return,
+ children_return, nchildren_return );
+ if( !result )
+ {
+ XklDebug( 160,
+ "Could not get tree info for window " WINID_FORMAT ": %d\n", w,
+ result );
+ _xklLastErrorMsg = "Could not get the tree info";
+ }
+
+ return result ? Success : FirstExtensionError;
+}
+
+/*
+ * Actually taken from mxkbledpanel, valueChangedProc
+ */
+Bool _XklSetIndicator( int indicatorNum, Bool set )
+{
+ XkbIndicatorMapPtr map;
+
+ map = _xklXkb->indicators->maps + indicatorNum;
+
+ /* The 'flags' field tells whether this indicator is automatic
+ * (XkbIM_NoExplicit - 0x80), explicit (XkbIM_NoAutomatic - 0x40),
+ * or neither (both - 0xC0).
+ *
+ * If NoAutomatic is set, the server ignores the rest of the
+ * fields in the indicator map (i.e. it disables automatic control
+ * of the LED). If NoExplicit is set, the server prevents clients
+ * from explicitly changing the value of the LED (using the core
+ * protocol *or* XKB). If NoAutomatic *and* NoExplicit are set,
+ * the LED cannot be changed (unless you change the map first).
+ * If neither NoAutomatic nor NoExplicit are set, the server will
+ * change the LED according to the indicator map, but clients can
+ * override that (until the next automatic change) using the core
+ * protocol or XKB.
+ */
+ switch ( map->flags & ( XkbIM_NoExplicit | XkbIM_NoAutomatic ) )
+ {
+ case XkbIM_NoExplicit | XkbIM_NoAutomatic:
+ {
+ // Can do nothing. Just ignore the indicator
+ return True;
+ }
+
+ case XkbIM_NoAutomatic:
+ {
+ if( _xklXkb->names->indicators[indicatorNum] != None )
+ XkbSetNamedIndicator( _xklDpy, XkbUseCoreKbd,
+ _xklXkb->names->indicators[indicatorNum], set,
+ False, NULL );
+ else
+ {
+ XKeyboardControl xkc;
+ xkc.led = indicatorNum;
+ xkc.led_mode = set ? LedModeOn : LedModeOff;
+ XChangeKeyboardControl( _xklDpy, KBLed | KBLedMode, &xkc );
+ XSync( _xklDpy, 0 );
+ }
+
+ return True;
+ }
+
+ case XkbIM_NoExplicit:
+ break;
+ }
+
+ /* The 'ctrls' field tells what controls tell this indicator to
+ * to turn on: RepeatKeys (0x1), SlowKeys (0x2), BounceKeys (0x4),
+ * StickyKeys (0x8), MouseKeys (0x10), AccessXKeys (0x20),
+ * TimeOut (0x40), Feedback (0x80), ToggleKeys (0x100),
+ * Overlay1 (0x200), Overlay2 (0x400), GroupsWrap (0x800),
+ * InternalMods (0x1000), IgnoreLockMods (0x2000),
+ * PerKeyRepeat (0x3000), or ControlsEnabled (0x4000)
+ */
+ if( map->ctrls )
+ {
+ unsigned long which = map->ctrls;
+
+ XkbGetControls( _xklDpy, XkbAllControlsMask, _xklXkb );
+ if( set )
+ _xklXkb->ctrls->enabled_ctrls |= which;
+ else
+ _xklXkb->ctrls->enabled_ctrls &= ~which;
+ XkbSetControls( _xklDpy, which | XkbControlsEnabledMask, _xklXkb );
+ }
+
+ /* The 'which_groups' field tells when this indicator turns on
+ * * for the 'groups' field: base (0x1), latched (0x2), locked (0x4),
+ * * or effective (0x8).
+ * */
+ if( map->groups )
+ {
+ int i;
+ unsigned int group = 1;
+
+ /* Turning on a group indicator is kind of tricky. For
+ * now, we will just Latch or Lock the first group we find
+ * if that is what this indicator does. Otherwise, we're
+ * just going to punt and get out of here.
+ */
+ if( set )
+ {
+ for( i = XkbNumKbdGroups; --i >= 0; )
+ if( ( 1 << i ) & map->groups )
+ {
+ group = i;
+ break;
+ }
+ if( map->which_groups & ( XkbIM_UseLocked | XkbIM_UseEffective ) )
+ {
+ // Important: Groups should be ignored here - because they are handled separately!
+ // XklLockGroup( group );
+ } else if( map->which_groups & XkbIM_UseLatched )
+ XkbLatchGroup( _xklDpy, XkbUseCoreKbd, group );
+ else
+ {
+ // Can do nothing. Just ignore the indicator
+ return True;
+ }
+ } else
+ /* Turning off a group indicator will mean that we just
+ * Lock the first group that this indicator doesn't watch.
+ */
+ {
+ for( i = XkbNumKbdGroups; --i >= 0; )
+ if( !( ( 1 << i ) & map->groups ) )
+ {
+ group = i;
+ break;
+ }
+ XklLockGroup( group );
+ }
+ }
+
+ /* The 'which_mods' field tells when this indicator turns on
+ * for the modifiers: base (0x1), latched (0x2), locked (0x4),
+ * or effective (0x8).
+ *
+ * The 'real_mods' field tells whether this turns on when one of
+ * the real X modifiers is set: Shift (0x1), Lock (0x2), Control (0x4),
+ * Mod1 (0x8), Mod2 (0x10), Mod3 (0x20), Mod4 (0x40), or Mod5 (0x80).
+ *
+ * The 'virtual_mods' field tells whether this turns on when one of
+ * the virtual modifiers is set.
+ *
+ * The 'mask' field tells what real X modifiers the virtual_modifiers
+ * map to?
+ */
+ if( map->mods.real_mods || map->mods.mask )
+ {
+ unsigned int affect, mods;
+
+ affect = ( map->mods.real_mods | map->mods.mask );
+
+ mods = set ? affect : 0;
+
+ if( map->which_mods & ( XkbIM_UseLocked | XkbIM_UseEffective ) )
+ XkbLockModifiers( _xklDpy, XkbUseCoreKbd, affect, mods );
+ else if( map->which_mods & XkbIM_UseLatched )
+ XkbLatchModifiers( _xklDpy, XkbUseCoreKbd, affect, mods );
+ else
+ {
+ return True;
+ }
+ }
+
+ return True;
+}
+
+const char *_XklGetEventName( int type )
+{
+ // Not really good to use the fact of consecutivity
+ // but X protocol is already standartized so...
+ static const char *evtNames[] = {
+ "KeyPress",
+ "KeyRelease",
+ "ButtonPress",
+ "ButtonRelease",
+ "MotionNotify",
+ "EnterNotify",
+ "LeaveNotify",
+ "FocusIn",
+ "FocusOut",
+ "KeymapNotify",
+ "Expose",
+ "GraphicsExpose",
+ "NoExpose",
+ "VisibilityNotify",
+ "CreateNotify",
+ "DestroyNotify",
+ "UnmapNotify",
+ "MapNotify",
+ "MapRequest",
+ "ReparentNotify",
+ "ConfigureNotify",
+ "ConfigureRequest",
+ "GravityNotify",
+ "ResizeRequest",
+ "CirculateNotify",
+ "CirculateRequest",
+ "PropertyNotify",
+ "SelectionClear",
+ "SelectionRequest",
+ "SelectionNotify",
+ "ColormapNotify", "ClientMessage", "MappingNotify", "LASTEvent"
+ };
+ type -= KeyPress;
+ if( type < 0 || type > ( sizeof( evtNames ) / sizeof( evtNames[0] ) ) )
+ return NULL;
+ return evtNames[type];
+}