diff options
author | Huang Peng <shawn.p.huang@gmail.com> | 2008-05-12 15:18:48 +0800 |
---|---|---|
committer | Huang Peng <shawn.p.huang@gmail.com> | 2008-05-12 15:18:48 +0800 |
commit | c25a24ae2e40a39db61fcd6f80d76469daa49a28 (patch) | |
tree | 8e9640375fcaf1778c264a5609bc94d499f240cc | |
download | ibus-c25a24ae2e40a39db61fcd6f80d76469daa49a28.tar.gz ibus-c25a24ae2e40a39db61fcd6f80d76469daa49a28.tar.xz ibus-c25a24ae2e40a39db61fcd6f80d76469daa49a28.zip |
import ibus
38 files changed, 4988 insertions, 0 deletions
@@ -0,0 +1 @@ +Huang Peng <shawn.p.huang@gmail.com> @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, 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 software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, 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 redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ChangeLog diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..acd6b64 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,42 @@ +# vim:set noet ts=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com> +# +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA +# +# $Id: $ +# +SUBDIRS = \ + gtk2 \ + m4 \ + po \ + $(NULL) + +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = \ + $(NULL) + +noinst_DIST = \ + $(NULL) + + +DISTCLEANFILES = \ + po/stamp-it + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..72d295d --- /dev/null +++ b/autogen.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -e +set -x + +autopoint +libtoolize --automake --copy +aclocal -I m4 +autoheader +gtkdocize --copy +automake --add-missing --copy +autoconf +./configure $* diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..f8ccc91 --- /dev/null +++ b/configure.ac @@ -0,0 +1,116 @@ +# vim:set noet ts=4: +# +# ibus - The Input Bus +# +# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com> +# +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA +# +# $Id: $ +# +AC_INIT([ibus],[0.0.1],[https://bugs.launchpad.net/ibus],[ibus]) + +AM_INIT_AUTOMAKE([1.10]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_GNU_SOURCE + +# define PACKAGE_VERSION_* variables +AS_VERSION +AS_NANO +AM_SANITY_CHECK +AM_MAINTAINER_MODE +AM_DISABLE_STATIC +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_ISC_POSIX +AC_HEADER_STDC +AM_PROG_LIBTOOL + + + +# check glib2 +AM_PATH_GLIB_2_0 +PKG_CHECK_MODULES(GLIB2, [ + glib-2.0 +]) +PKG_CHECK_MODULES(GOBJECT2, [ + gobject-2.0 +]) +PKG_CHECK_MODULES(PYGOBJECT2, [ + pygobject-2.0 +]) + + +# check gtk & pygtk +PKG_CHECK_MODULES(GTK2, [ + gtk+-2.0 +]) + +PKG_CHECK_MODULES(PYGTK2, [ + pygtk-2.0 +]) + +# check dbus-glib +PKG_CHECK_MODULES(DBUS, [ + dbus-glib-1 +]) + + +AC_PATH_PROG(PYGTK_CODEGEN, pygtk-codegen-2.0, no) + +# check python +AM_PATH_PYTHON([2.5]) +PYTHON_CONFIG=`type -p python$PYTHON_VERSION-config` +if test "$PYTHON_CONFIG" != ""; then + PYTHON_CFLAGS=`$PYTHON_CONFIG --includes` + PYTHON_LIBS=`$PYTHON_CONFIG --libs` +else + PYTHON_CFLAGS=`$PYTHON $srcdir/python-config.py --includes` + PYTHON_LIBS=`$PYTHON $srcdir/python-config.py --libs` +fi +AC_SUBST(PYTHON_CFLAGS) +AC_SUBST(PYTHON_LIBS) + +# Checks for gtk-doc +GTK_DOC_CHECK([1.6]) + +AC_CHECK_PROG(DB2HTML, db2html, true, false) +AM_CONDITIONAL(HAVE_DOCBOOK, $DB2HTML) + + +# define GETTEXT_* variables +GETTEXT_PACKAGE=ibus +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Package name for gettext]) + +AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_VERSION(0.16.1) + +# Args for make distcheck +DISTCHECK_CONFIGURE_FLAGS="--enable-gtk-doc" +AC_SUBST(DISTCHECK_CONFIGURE_FLAGS) + +# OUTPUT files +AC_CONFIG_FILES([ po/Makefile.in +Makefile +gtk2/Makefile +m4/Makefile +]) + +AC_OUTPUT diff --git a/doc/interfaces b/doc/interfaces new file mode 100644 index 0000000..444e775 --- /dev/null +++ b/doc/interfaces @@ -0,0 +1,63 @@ + +Type Name: ObjectID +Desc: It is a String to identify a dbus object. Like: "object_path@bus_name" + +Bus Name: org.freedesktop.IM +Name: IDirectory +Path: /org/freedesktop/IM/Directory + It is responeible for maintain all object in the virtual objects tree. +Methods: + void register_object (String object_path) + void register_objects (String *object_path_array) + void deregister_object (String object_path) + void deregister_object (String object_path) + void deregisters_object (String *object_path_array) + ObjectID *list_objects (String dirname) + Return Object_ids in a folder specified by argument dirname + +Signals: + directory_changed (String dirname) + +Name: IManager +Path: /org/freedesktop/IM/Manager + Manager is responsible for managering all engines & im clients. +Methods: + void register_client (String client_name) + register the client (the caller) to the Manager, the client_name is a readable name of client. It do not specify it, manager will use caller's bus_name. + void deregister_client () + deregister the client (the caller) in the Manager. The manager should release resources hold by the client. + void set_client_name () + Set a associated name for the client (the caller). Like: gedit-pid + String *get_hotkeys () + Retrun a String array. Each string in the array is a hotkey. When user press the hotkeys, client should send the hotket's index in this array to the manager. + void focus_in () + Tell the manager the client (the caller) request focus in. The current focused client will be focus out. + void focus_out () + Tell the manager the client (the caller) request focus out. + String get_focused_client () + +Signals: + focus_changed (String client_name, String engine_path) + +Name: IFactory +Name: IEngineFactory +Name: IEngine +Methods: + boolean filter_keypress (unsigned int state, unsigned int keyval, boolean is_press) + void focus_in () + void focus_out () + void reset () +??void set_set_client_window + (int window_id) + void set_cursor_position (int x, int y) + void set_use_preedit (boolean use_preedit) + void set_surrounding (String text, int cursor_index) +Signal: + commit (String commit_string) + preedit_start () + preedit_end () + preedit_changed (String preedit_string, Attribute *attrs) + retrieve_surrounding () + delete_surrounding (int offset, int n_chars) + + diff --git a/doc/specification b/doc/specification new file mode 100644 index 0000000..f83d0c0 --- /dev/null +++ b/doc/specification @@ -0,0 +1,19 @@ +IM Specification + + Huang Peng <phuang@redhat.com> + +1. Introducation + About + Architecture + + +Chapter 1 Introducation + +About + +This document contain the specification of IM which is a software that managers all input services in the system. + +Architecture + +IM manager provides a directory service. All IM services will register themself in this directory service. All clients can enumerate serivces in a specified directoy folder and get a nodification when a specified folder's contain be changed. + diff --git a/gtk2/Makefile.am b/gtk2/Makefile.am new file mode 100644 index 0000000..8385284 --- /dev/null +++ b/gtk2/Makefile.am @@ -0,0 +1,56 @@ +# vim:set noet ts=4: +# +# gik - The G Input Toolkit +# +# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com> +# +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA +# +# $Id: $ +# +INCLUDES = \ + -I$(top_srcdir)/src + +im_gik_sources = \ + gikim.c \ + gikimclient.c \ + gikimcontext.c \ + $(NULL) + +im_gik_private_headers = \ + gikimcontext.h \ + gikimclient.h \ + $(NULL) + +immoduledir = $(libdir)/gtk-2.0/immodules +immodule_LTLIBRARIES = im-gik.la + +im_gik_la_SOURCES = $(im_gik_sources) $(im_gik_private_headers) +im_gik_la_CFLAGS = \ + @GTK2_CFLAGS@ \ + @DBUS_CFLAGS@ \ + -DG_LOG_DOMAIN=\"GIK\" \ + $(NULL) + +im_gik_la_LDFLAGS = \ + @GTK2_LIBS@ \ + @DBUS_LIBS@ \ + -avoid-version \ + -module \ + $(NULL) + +EXTRA_DIST = diff --git a/gtk2/gikim.c b/gtk2/gikim.c new file mode 100644 index 0000000..e394a78 --- /dev/null +++ b/gtk2/gikim.c @@ -0,0 +1,68 @@ +/* vim:set et ts=4: */ +/* GIK - The G Input Toolkit + * Copyright (C) 2008-2009 Huang Peng + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <gtk/gtk.h> +#include <gtk/gtkimmodule.h> +#include <string.h> +#include <stdio.h> +#include "gikimclient.h" +#include "gikimcontext.h" + +#define GIK_LOCALDIR "" + +static const GtkIMContextInfo gik_im_info = { + "gik", + "The G Input Toolkit", + "gik", + GIK_LOCALDIR, + "" +}; + +static const GtkIMContextInfo * info_list[] = { + &gik_im_info +}; + +void +im_module_init (GTypeModule *type_module) +{ + gik_im_client_register_type(type_module); + gik_im_context_register_type(type_module); +} + +void +im_module_exit (void) +{ +} + +GtkIMContext * +im_module_create (const gchar *context_id) +{ + if (strcmp (context_id, "gik") == 0) + return gik_im_context_new (); + return NULL; +} + +void +im_module_list (const GtkIMContextInfo ***contexts, + int *n_contexts) +{ + *contexts = info_list; + *n_contexts = G_N_ELEMENTS (info_list); +} + diff --git a/gtk2/gikimclient.c b/gtk2/gikimclient.c new file mode 100644 index 0000000..f022242 --- /dev/null +++ b/gtk2/gikimclient.c @@ -0,0 +1,1049 @@ +/* vim:set et ts=4: */ +/* GIK - The G Input Toolkit + * Copyright (C) 2008-2009 Huang Peng + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <string.h> +#include <stdarg.h> +#include "gikimclient.h" + +#define IBUS_NAME "org.freedesktop.IBus" +#define IBUS_IFACE "org.freedesktop.IBus" +#define IBUS_PATH "/org/freedesktop/IBus" +#define IBUS_ADDR "unix:abstract=/tmp/ibus" +//#define IBUS_ADDR "tcp:host=localhost,port=7799" + +/* GikIMClientPriv */ +struct _GikIMClientPrivate { +#if USE_DBUS_SESSION_BUS + DBusConnection *dbus; +#endif + DBusConnection *ibus; + gboolean enable; + + GtkIMContext *context; + + /* preedit status */ + gchar *preedit_string; + PangoAttrList *preedit_attrs; + gint preedit_cursor; +}; + +/* functions prototype */ +static void gik_im_client_class_init (GikIMClientClass *klass); +static void gik_im_client_init (GikIMClient *client); +static void gik_im_client_finalize (GObject *obj); + +static void gik_im_client_commit_string(GikIMClient *client, + const gchar *string); +static void gik_im_client_preedit_changed + (GikIMClient *client, + const gchar *string, + PangoAttrList *attrs, + gint cursor_pos); + +static void gik_im_client_sync_hotkeys (GikIMClient *client); +static gboolean _ibus_call_with_reply_and_block + (DBusConnection *connection, + const gchar *method, + int first_arg_type, + ...); +static gboolean _ibus_call_with_reply (DBusConnection *connection, + const gchar *method, + DBusPendingCallNotifyFunction + function, + void *data, + DBusFreeFunction free_function, + int first_arg_type, + ...); +static gboolean _dbus_call_with_reply_and_block + (DBusConnection *connection, + const gchar *dest, + const gchar *path, + const gchar *iface, + const char *method, + gint first_arg_type, + ...); + +/* callback functions */ +static DBusHandlerResult + _gik_im_client_message_filter_cb + (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +static void _dbus_name_owner_changed_cb (DBusGProxy *proxy, + const gchar *name, + const gchar *old_name, + const gchar *new_name, + GikIMClient *client); + +static GType gik_type_im_client = 0; +static GtkObjectClass *parent_class = NULL; +static GikIMClient *_client = NULL; +static gboolean has_focus = FALSE; + + +GType +gik_im_client_get_type (void) +{ + g_assert (gik_type_im_client != 0); + return gik_type_im_client; +} + +void +gik_im_client_register_type (GTypeModule *type_module) +{ + static const GTypeInfo gik_im_client_info = { + sizeof (GikIMClientClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gik_im_client_class_init, + NULL, /* class finialize */ + NULL, /* class data */ + sizeof (GikIMClient), + 0, + (GInstanceInitFunc) gik_im_client_init, + }; + + if (! gik_type_im_client ) { + gik_type_im_client = + g_type_module_register_type (type_module, + GTK_TYPE_OBJECT, + "GikIMClient", + &gik_im_client_info, + (GTypeFlags)0); + } +} + + +GikIMClient * +gik_im_client_get_client (void) +{ + if (_client == NULL) { + _client = GIK_IM_CLIENT(g_object_new (GIK_TYPE_IM_CLIENT, NULL)); + g_object_ref_sink (_client); + } + else { + g_object_ref (_client); + } + + return _client; +} + + +static void +gik_im_client_class_init (GikIMClientClass *klass) +{ + GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + parent_class = (GtkObjectClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (GikIMClientPrivate)); + + gobject_class->finalize = &gik_im_client_finalize; +} + +#if 0 +static void +_gik_im_client_reinit_imm (GikIMClient *client) +{ + GError *error; + GikIMClientPrivate *priv = client->priv; + + if (priv->imm != NULL) { + g_object_unref (priv->imm); + } + + /* get gik proxy */ + error = NULL; + priv->imm = dbus_g_proxy_new_for_name_owner (priv->ibus, + GIK_DBUS_SERVICE, + GIK_DBUS_PATH, + GIK_DBUS_INTERFACE, + &error); + + if (priv->imm == NULL) { + g_warning (error->message); + g_error_free (error); + return; + } + + error = NULL; + if (!dbus_g_proxy_call (priv->imm, "register_client", &error, + G_TYPE_INVALID, G_TYPE_INVALID)) { + g_warning ("%s", error->message); + g_error_free (error); + g_object_unref (priv->imm); + priv->imm = NULL; + return; + } + + gik_im_client_sync_hotkeys (client); + g_debug ("new imm %s", dbus_g_proxy_get_bus_name (priv->imm)); +} +#endif + +static void +_gik_im_client_ibus_open (GikIMClient *client) +{ + gchar *ibus_addr; + DBusError error; + + GikIMClientPrivate *priv; + priv = G_TYPE_INSTANCE_GET_PRIVATE (client, GIK_TYPE_IM_CLIENT, GikIMClientPrivate); + +#if USE_DBUS_SESSION_BUS + dbus_connection_setup_with_g_main (priv->dbus, NULL); + if (!_dbus_call_with_reply_and_block (priv->dbus, + IBUS_NAME, IBUS_PATH, IBUS_IFACE, + "GetIBusAddress", + DBUS_TYPE_INVALID, + DBUS_TYPE_STRING, &ibus_addr, + DBUS_TYPE_INVALID + )) { + g_warning ("Can not get ibus address"); + return; + } +#endif + + /* + * Init ibus and proxy object + */ + dbus_error_init (&error); + priv->ibus = dbus_connection_open_private (IBUS_ADDR, &error); + if (priv->ibus == NULL) { + g_warning ("Error: %s", error.message); + dbus_error_free (&error); + return; + } + + if (!dbus_connection_add_filter (priv->ibus, + _gik_im_client_message_filter_cb, + client, NULL)) { + g_warning ("Out of memory"); + return; + } + dbus_connection_setup_with_g_main (priv->ibus, NULL); + const gchar *app_name = g_get_application_name (); + _ibus_call_with_reply_and_block (priv->ibus, "RegisterClient", + DBUS_TYPE_STRING, &app_name, + DBUS_TYPE_INVALID, + DBUS_TYPE_INVALID); + +} + +static void +_gik_im_client_ibus_close (GikIMClient *client) +{ + DBusError error; + + GikIMClientPrivate *priv; + priv = G_TYPE_INSTANCE_GET_PRIVATE (client, GIK_TYPE_IM_CLIENT, GikIMClientPrivate); + + if (priv->ibus) { + dbus_connection_close (priv->ibus); + dbus_connection_unref (priv->ibus); + priv->ibus = None; + } +} + +static void +gik_im_client_init (GikIMClient *obj) +{ + DEBUG_FUNCTION_IN; + + DBusError error; + GikIMClient *client = GIK_IM_CLIENT (obj); + GikIMClientPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (client, GIK_TYPE_IM_CLIENT, GikIMClientPrivate); + client->priv = priv; + + priv->context = NULL; + + priv->preedit_string = NULL; + priv->preedit_attrs = NULL; + priv->preedit_cursor = 0; + priv->enable = FALSE; + +#if USE_DBUS_SESSION_BUS + /* + * Init dbus + */ + dbus_error_init (&error); + priv->dbus = dbus_bus_get (DBUS_BUS_SESSION, &error); + if (priv->dbus == NULL) { + g_warning ("Error: %s", error.message); + dbus_error_free (&error); + return; + } +#endif + + _gik_im_client_ibus_open (client); + +#if USE_DBUS_SESSION_BUS + if (!dbus_connection_add_filter (priv->dbus, + _gik_im_client_message_filter_cb, + client, NULL)) { + g_warning ("Out of memory"); + return; + } + + gchar *rule = + "type='signal'," + "sender='" DBUS_SERVICE_DBUS "'," + "interface='" DBUS_INTERFACE_DBUS "'," + "member='NameOwnerChanged'," + "path='" DBUS_PATH_DBUS "'," + "arg0='" IBUS_NAME "'"; + + if (!_dbus_call_with_reply_and_block (priv->dbus, + DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, + "AddMatch", + DBUS_TYPE_STRING, &rule, + DBUS_TYPE_INVALID, + DBUS_TYPE_INVALID + )) { + g_warning ("Can not get ibus address"); + return; + } +#endif +#if 0 + /* get dbus proxy */ + priv->dbus = dbus_g_proxy_new_for_name (priv->ibus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + g_assert (priv->dbus != NULL); + + /* connect NameOwnerChanged signal */ + dbus_g_proxy_add_signal (priv->dbus, "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->dbus, "NameOwnerChanged", + G_CALLBACK (_dbus_name_owner_changed_cb), + (gpointer)client, NULL); + dbus_bus_add_match ((DBusConnection *)dbus_g_connection_get_connection (priv->ibus), + "type='signal'," + "sender='" DBUS_SERVICE_DBUS + "',interface='" DBUS_INTERFACE_DBUS + "',path='" DBUS_PATH_DBUS + "',member='NameOwnerChanged'," + "arg0='" GIK_DBUS_SERVICE "'", + &dbus_error); + + _gik_im_client_reinit_imm (client); +#endif + +} + + +static void +gik_im_client_finalize (GObject *obj) +{ + DEBUG_FUNCTION_IN; + + GikIMClient *client = GIK_IM_CLIENT (obj); + GikIMClientPrivate *priv = client->priv; + + g_assert (client == _client); + + if (priv->preedit_string) { + g_free (priv->preedit_string); + } + + if (priv->preedit_attrs) { + pango_attr_list_unref (priv->preedit_attrs); + } +#if USE_DBUS_SESSION_BUS + if (priv->dbus) { + dbus_connection_unref (priv->dbus); + } +#endif + _gik_im_client_ibus_close (client); + + G_OBJECT_CLASS(parent_class)->finalize (obj); + + _client = NULL; +} + + +void +gik_im_client_set_im_context (GikIMClient *client, GtkIMContext *context) +{ + GikIMClientPrivate *priv = client->priv; + priv->context = context; +} + +GtkIMContext * +gik_im_client_get_im_context (GikIMClient *client) +{ + GikIMClientPrivate *priv = client->priv; + return priv->context; +} + +static void +gik_im_client_commit_string (GikIMClient *client, const gchar *string) +{ + GikIMClientPrivate *priv = client->priv; + + if (priv->context) { + g_signal_emit_by_name (priv->context, "commit", string); + } +} + +static void +gik_im_client_preedit_changed (GikIMClient *client, const gchar *string, PangoAttrList *attrs, gint cursor_pos) +{ + GikIMClientPrivate *priv = client->priv; + if (priv->preedit_string) { + g_free (priv->preedit_string); + } + priv->preedit_string = g_strdup (string); + + if (priv->preedit_attrs) { + pango_attr_list_unref (priv->preedit_attrs); + } + + priv->preedit_attrs = attrs; + if (attrs) { + pango_attr_list_ref (priv->preedit_attrs); + } + + priv->preedit_cursor = cursor_pos; + if (priv->context) { + g_signal_emit_by_name (priv->context, "preedit-changed"); + } +} + +static void +_gik_signal_commit_string_handler (DBusConnection *connection, DBusMessage *message, GikIMClient *client) +{ + /* Handle CommitString signal */ + DBusError error = {0}; + gchar *string = NULL; + + if (!dbus_message_get_args (message, &error, + DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID)) { + g_warning ("%s", error.message); + dbus_error_free (&error); + } + else { + gik_im_client_commit_string (client, string); + } +} + +static void +_gik_signal_preedit_changed_handler (DBusConnection *connection, DBusMessage *message, GikIMClient *client) +{ + /* Handle PreeditChanged signal */ + DBusError error = {0}; + gchar *string = NULL; + gint cursor = 0; + DBusMessageIter iter, sub_iter; + gint type, sub_type; + PangoAttrList *attrs = NULL; + + if (!dbus_message_iter_init (message, &iter)) { + g_warning ("The PreeditChanged signal does have args!"); + return; + } + + type = dbus_message_iter_get_arg_type (&iter); + if (type != DBUS_TYPE_STRING) { + g_warning ("The frist argument of PreeditChanged signal must be a String"); + return; + } + dbus_message_iter_get_basic (&iter, &string); + dbus_message_iter_next (&iter); + + + type = dbus_message_iter_get_arg_type (&iter); + if (type != DBUS_TYPE_ARRAY) { + g_warning ("The secode argument of PreeditChanged signal must be a Struct Array"); + return; + } + dbus_message_iter_recurse (&iter, &sub_iter); + if (dbus_message_iter_get_arg_type (&sub_iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type (&sub_iter) != DBUS_TYPE_UINT32 ) { + g_warning ("The secode argument of PreeditChanged signal must be a Struct Array"); + return; + } + + attrs = pango_attr_list_new (); + while ((sub_type = dbus_message_iter_get_arg_type (&sub_iter) != DBUS_TYPE_INVALID)) { + PangoAttribute *attr; + DBusMessageIter sub_sub_iter; + guint *values = NULL; + gint length = 0; + dbus_message_iter_recurse (&sub_iter, &sub_sub_iter); + dbus_message_iter_get_fixed_array (&sub_sub_iter, &values, &length); + + if (length <= 0) { + g_warning ("The element of the second argument of PreeditChanged should not be a empty array"); + continue; + } + + switch (values[0]) { + case 1: /* Decoration */ + attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); + attr->start_index = values[2]; + attr->end_index = values[3]; + pango_attr_list_insert (attrs, attr); + break; + + case 2: /* Foreground Color */ + attr = pango_attr_foreground_new ( + (values[1] & 0xff0000) >> 8, + (values[1] & 0x00ff00), + (values[1] & 0x0000ff) << 8 + ); + attr->start_index = values[2]; + attr->end_index = values[3]; + pango_attr_list_insert (attrs, attr); + break; + case 3: /* Background Color */ + attr = pango_attr_background_new ( + (values[1] & 0xff0000) >> 8, + (values[1] & 0x00ff00), + (values[1] & 0x0000ff) << 8 + ); + attr->start_index = values[2]; + attr->end_index = values[3]; + pango_attr_list_insert (attrs, attr); + break; + default: + g_warning ("Unkown type attribute type = %d", values[0]); + + } + + dbus_message_iter_next (&sub_iter); + + } + dbus_message_iter_next (&iter); + + type = dbus_message_iter_get_arg_type (&iter); + if (type != DBUS_TYPE_INT32) { + g_warning ("The third argument of PreeditChanged signal must be an Int32"); + pango_attr_list_unref (attrs); + return; + } + dbus_message_iter_get_basic (&iter, &cursor); + dbus_message_iter_next (&iter); + + gik_im_client_preedit_changed (client, string, attrs, cursor); + pango_attr_list_unref (attrs); + +} + +static void +_gik_signal_name_owner_changed_handler (DBusConnection *connection, DBusMessage *message, GikIMClient *client) +{ + GikIMClientPrivate *priv = client->priv; + + gchar *name = NULL; + gchar *old_name = NULL; + gchar *new_name = NULL; + DBusError error = {0}; + + if (!dbus_message_get_args (message, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_name, + DBUS_TYPE_STRING, &new_name, + DBUS_TYPE_INVALID)) { + g_warning ("%s", error.message); + dbus_error_free (&error); + } + + g_return_if_fail (strcmp (name, IBUS_NAME) == 0); + + if (strcmp (new_name, "") == 0) { + _gik_im_client_ibus_close (client); + priv->enable = FALSE; + } + else { + _gik_im_client_ibus_open (client); + priv->enable = TRUE; + } +} + +static void +_gik_signal_enabled_handler (DBusConnection *connection, DBusMessage *message, GikIMClient *client) +{ + DEBUG_FUNCTION_IN; + client->priv->enable = TRUE; +} + + +static void +_gik_signal_disabled_handler (DBusConnection *connection, DBusMessage *message, GikIMClient *client) +{ + DEBUG_FUNCTION_IN; + client->priv->enable = FALSE; +} +static DBusHandlerResult +_gik_im_client_message_filter_cb (DBusConnection *connection, DBusMessage *message, void *user_data) +{ + GikIMClient *client = (GikIMClient *) user_data; + + static struct SIGNAL_HANDLER { + const gchar *iface; + const gchar *name; + void (* handler) (DBusConnection *, DBusMessage *, GikIMClient *); + } handlers[] = { + { DBUS_INTERFACE_DBUS, "NameOwnerChanged", _gik_signal_name_owner_changed_handler }, + { IBUS_IFACE, "CommitString", _gik_signal_commit_string_handler }, + { IBUS_IFACE, "PreeditChanged", _gik_signal_preedit_changed_handler }, + { IBUS_IFACE, "Enabled", _gik_signal_enabled_handler }, + { IBUS_IFACE, "Disabled", _gik_signal_disabled_handler }, + {0}, + }; + + gint i; + for (i = 0; handlers[i].iface != NULL; i++) { + if (dbus_message_is_signal (message, handlers[i].iface, handlers[i].name)) { + handlers[i].handler (connection, message, client); + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +inline static gboolean +_dbus_call_with_reply_and_block_valist (DBusConnection *connection, + const gchar *dest, const gchar *path, const gchar* iface, const char *method, + gint first_arg_type, va_list args) +{ + DBusMessage *message, *reply; + DBusError error = {0}; + int type; + + g_return_val_if_fail (connection != NULL, FALSE); + + message = dbus_message_new_method_call (dest, + path, iface, method); + if (!message) { + g_warning ("Out of memory!"); + return FALSE; + } + + if (!dbus_message_append_args_valist (message, first_arg_type, args)) { + dbus_message_unref (message); + g_warning ("Can not create call message"); + return FALSE; + } + + reply = dbus_connection_send_with_reply_and_block (connection, + message, -1, &error); + + dbus_message_unref (message); + + if (!reply) { + g_warning ("%s", error.message); + dbus_error_free (&error); + return FALSE; + } + + type = first_arg_type; + while (type != DBUS_TYPE_INVALID) { + if (type == DBUS_TYPE_ARRAY) { + va_arg (args, int); + va_arg (args, void *); + va_arg (args, int); + } + else { + va_arg (args, int); + } + type = va_arg (args, int); + } + + type = va_arg (args, int); + if (!dbus_message_get_args_valist (reply, &error, type, args)) { + g_warning ("%s", error.message); + dbus_error_free (&error); + dbus_message_unref (reply); + return FALSE; + } + dbus_message_unref (reply); + + return TRUE; + +} + +inline static gboolean +_dbus_call_with_reply_and_block (DBusConnection *connection, + const gchar *dest, const gchar *path, const gchar* iface, const char *method, + gint first_arg_type, ...) +{ + va_list args; + gboolean retval; + + va_start (args, first_arg_type); + retval = _dbus_call_with_reply_and_block_valist (connection, + dest, path, iface, method, first_arg_type, args); + va_end (args); + + return TRUE; + +} + +static gboolean +_ibus_call_with_reply_and_block (DBusConnection *connection, const gchar *method, int first_arg_type, ...) +{ + va_list args; + gboolean retval; + + va_start (args, first_arg_type); + retval = _dbus_call_with_reply_and_block_valist (connection, + IBUS_NAME, IBUS_PATH, IBUS_IFACE, method, first_arg_type, args); + va_end (args); + + return retval; + +} + + +inline static gboolean +_dbus_call_with_reply_valist (DBusConnection *connection, + const gchar *dest, const gchar *path, const gchar* iface, const char *method, + DBusPendingCallNotifyFunction notify_function, + void *user_data, DBusFreeFunction free_function, + gint first_arg_type, va_list args) +{ + DBusMessage *message = NULL; + DBusPendingCall *pendingcall = NULL; + DBusError error = {0}; + int type; + + if (connection == NULL) { + g_warning ("connection != NULL failed"); + goto error; + + } + + message = dbus_message_new_method_call (dest, + path, iface, method); + if (!message) { + g_warning ("Out of memory!"); + goto error; + } + + if (!dbus_message_append_args_valist (message, first_arg_type, args)) { + g_warning ("Can not create call message"); + goto error; + } + + if (!dbus_connection_send_with_reply (connection, + message, &pendingcall, -1)) { + g_warning ("Out of memory!"); + goto error; + } + + if (!dbus_pending_call_set_notify (pendingcall, notify_function, + user_data, free_function)) { + g_warning ("Out of memory!"); + goto error; + } + + dbus_message_unref (message); + return TRUE; + +error: + if (message) + dbus_message_unref (message); + if (pendingcall) + dbus_pending_call_cance (pendingcall); + if (user_data && free_function) + free_function (user_data); + return False; +} + +inline static gboolean +_dbus_call_with_reply (DBusConnection *connection, + const gchar *dest, const gchar *path, const gchar* iface, const char *method, + DBusPendingCallNotifyFunction notify_function, + void *user_data, DBusFreeFunction free_function, + gint first_arg_type, ...) +{ + va_list args; + gboolean retval; + + va_start (args, first_arg_type); + retval = _dbus_call_with_reply_valist (connection, + dest, path, iface, method, + notify_function, + user_data, free_function, + first_arg_type, args); + va_end (args); + + return TRUE; + +} + + + +static gboolean +_ibus_call_with_reply (DBusConnection *connection, const gchar *method, + DBusPendingCallNotifyFunction notify_function, + void *user_data, DBusFreeFunction free_function, + int first_arg_type, ...) +{ + va_list args; + gboolean retval; + + va_start (args, first_arg_type); + retval = _dbus_call_with_reply_valist (connection, + IBUS_NAME, IBUS_PATH, IBUS_IFACE, + method, notify_function, + user_data, free_function, + first_arg_type, args); + va_end (args); + + return retval; +} + + +static void +_gik_filter_keypress_reply_cb (DBusPendingCall *pending, void *user_data) +{ + DBusMessage *reply; + DBusError error = {0}; + GdkEvent *event = (GdkEvent *) user_data; + gboolean retval; + + + reply = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + + if (dbus_set_error_from_message (&error, reply)) { + g_warning ("%s", error.message); + dbus_error_free (&error); + retval = FALSE; + } + else { + if (!dbus_message_get_args (reply, &error, + DBUS_TYPE_BOOLEAN, &retval, DBUS_TYPE_INVALID)) { + g_warning ("%s", error.message); + dbus_error_free (&error); + retval = FALSE; + } + } + + if (!retval) { + event->any.send_event = TRUE; + gdk_event_put (event); + } +} + +gboolean +gik_im_client_filter_keypress (GikIMClient *client, GdkEventKey *event) +{ + GikIMClientPrivate *priv = client->priv; + + guint state = event->state & GDK_MODIFIER_MASK; + gboolean is_press = event->type == GDK_KEY_PRESS; + + if (event->send_event) { + return FALSE; + } + + /* Call IBus ProcessKeyEvent method */ + if (!_ibus_call_with_reply (priv->ibus, + "ProcessKeyEvent", + _gik_filter_keypress_reply_cb, + gdk_event_copy ((GdkEvent *)event), + (DBusFreeFunction)gdk_event_free, + DBUS_TYPE_UINT32, &event->keyval, + DBUS_TYPE_BOOLEAN, &is_press, + DBUS_TYPE_UINT32, &state, + DBUS_TYPE_INVALID)) + return FALSE; + + return TRUE; +} + + +void +gik_im_client_focus_in (GikIMClient *client) +{ + /* Call IBus FocusIn method */ + _ibus_call_with_reply_and_block (client->priv->ibus, + "FocusIn", + DBUS_TYPE_INVALID, + DBUS_TYPE_INVALID); +} + +void +gik_im_client_focus_out (GikIMClient *client) +{ + /* Call IBus FocusOut method */ + _ibus_call_with_reply_and_block (client->priv->ibus, + "FocusOut", + DBUS_TYPE_INVALID, + DBUS_TYPE_INVALID); + +} + +void +gik_im_client_reset (GikIMClient *client) +{ + /* Call IBus Reset method */ + _ibus_call_with_reply_and_block (client->priv->ibus, + "Reset", + DBUS_TYPE_INVALID, + DBUS_TYPE_INVALID); + +} + + +gboolean +gik_im_client_get_preedit_string ( + GikIMClient *client, + gchar **str, + PangoAttrList **attrs, + gint *cursor_pos +) +{ + GikIMClientPrivate *priv = client->priv; + + if (str) { + *str = g_strdup (priv->preedit_string ? priv->preedit_string: ""); + } + + if (attrs) { + if (priv->preedit_attrs) { + *attrs = pango_attr_list_ref (priv->preedit_attrs); + } + else { + *attrs = pango_attr_list_new (); + } + } + + if (cursor_pos) { + *cursor_pos = priv->preedit_cursor; + } + + return TRUE; +} + + +void +gik_im_client_set_client_window (GikIMClient *client, GdkWindow *window) +{ + GikIMClientPrivate *priv = client->priv; +} + +void +gik_im_client_set_cursor_location (GikIMClient *client, GdkRectangle *area) +{ + GikIMClientPrivate *priv = client->priv; + + _ibus_call_with_reply_and_block (client->priv->ibus, + "SetCursorLocation", + DBUS_TYPE_INT32, &area->x, + DBUS_TYPE_INT32, &area->y, + DBUS_TYPE_INT32, &area->width, + DBUS_TYPE_INT32, &area->height, + DBUS_TYPE_INVALID, + DBUS_TYPE_INVALID); +} + + +gboolean +gik_im_client_is_enabled (GikIMClient *client) +{ + return (client->priv->ibus != NULL) && (client->priv->enable); +} + +static void +gik_im_client_sync_hotkeys (GikIMClient *client) +{ + GError *error; + gchar **hotkeys = NULL; + gint i; + + GikIMClientPrivate *priv = client->priv; +#if 0 + g_return_if_fail (priv->imm != NULL); + + error = NULL; + if (!dbus_g_proxy_call (priv->imm, "get_hotkeys", &error, + G_TYPE_INVALID, + G_TYPE_STRV, &hotkeys, + G_TYPE_INVALID)) { + if (error) { + g_warning ("%s", error->message); + g_error_free (error); + } + return; + } + + for (i = 0; i < g_strv_length (hotkeys); i++) { + g_debug ("hotkeys[%d] = %s", i, hotkeys[i]); + } + g_strfreev (hotkeys); +#endif + +} + +/* Callback functions for slave context */ +#if 0 +static void +_dbus_name_owner_changed_cb ( + DBusGProxy *proxy, + const gchar *name, + const gchar *prev_owner, + const gchar *new_owner, + GikIMClient *client) +{ + GikIMClientPrivate *priv = client->priv; + + if (strcmp (name, GIK_DBUS_SERVICE) == 0) { + if (strcmp (new_owner, "") == 0) { + /* Gik service exited */ + if (priv->imm) { + g_object_unref (priv->imm); + priv->imm = NULL; + } + if (priv->ime) { + g_object_unref (priv->ime); + priv->ime = NULL; + } + } + else { + /* Gik service avaliable or owner changed */ + _gik_im_client_reinit_imm (client); + } + } +} + +#endif diff --git a/gtk2/gikimclient.h b/gtk2/gikimclient.h new file mode 100644 index 0000000..ba55448 --- /dev/null +++ b/gtk2/gikimclient.h @@ -0,0 +1,98 @@ +/* vim:set et ts=4: */ +/* GIK - The G Input Toolkit + * Copyright (C) 2008-2009 Huang Peng + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GIK_IM_CLIENT_H_ +#define __GIK_IM_CLIENT_H_ + +#include <gtk/gtk.h> + +/* + * Type macros. + */ + +/* define GOBJECT macros */ +#define GIK_TYPE_IM_CLIENT \ + (gik_im_client_get_type ()) +#define GIK_IM_CLIENT(obj) \ + (GTK_CHECK_CAST ((obj), GIK_TYPE_IM_CLIENT, GikIMClient)) +#define GIK_IM_CLIENT_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST ((klass), GIK_TYPE_IM_CLIENT, GikIMClientClass)) +#define GIK_IS_IM_CLIENT(obj) \ + (GTK_CHECK_TYPE ((obj), GIK_TYPE_IM_CLIENT)) +#define GIK_IS_IM_CLIENT_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE ((klass), GIK_TYPE_IM_CLIENT)) +#define GIK_IM_CLIENT_GET_CLASS(obj) \ + (GTK_CHECK_GET_CLASS ((obj), GIK_TYPE_IM_CLIENT, GikIMClientClass)) + +#if 0 +#define DEBUG_FUNCTION_IN g_debug("%s IN", __FUNCTION__); +#define DEBUG_FUNCTION_OUT g_debug("%s OUT", __FUNCTION__); +#else +#define DEBUG_FUNCTION_IN +#define DEBUG_FUNCTION_OUT +#endif + + + +#define GIK_DBUS_SERVICE "org.freedesktop.gik" +#define GIK_DBUS_INTERFACE "org.freedesktop.gik.Manager" +#define GIK_DBUS_PATH "/org/freedesktop/gik/Manager" + +G_BEGIN_DECLS +typedef struct _GikIMClient GikIMClient; +typedef struct _GikIMClientClass GikIMClientClass; +typedef struct _GikIMClientPrivate GikIMClientPrivate; + +struct _GikIMClient { + GtkObject parent; + /* instance members */ + GikIMClientPrivate *priv; +}; + +struct _GikIMClientClass { + GtkObjectClass parent; + /* class members */ +}; + +GType gik_im_client_get_type (void); +GikIMClient *gik_im_client_get_client (void); +void gik_im_client_register_type (GTypeModule *type_module); +void gik_im_client_shutdown (void); +void gik_im_client_focus_in (GikIMClient *client); +void gik_im_client_focus_out (GikIMClient *client); +void gik_im_client_set_im_context (GikIMClient *client, + GtkIMContext *context); +GtkIMContext *gik_im_client_get_im_context (GikIMClient *client); +void gik_im_client_reset (GikIMClient *client); +gboolean gik_im_client_filter_keypress (GikIMClient *client, + GdkEventKey *key); +gboolean gik_im_client_get_preedit_string + (GikIMClient *client, + gchar **str, + PangoAttrList **attrs, + gint *cursor_pos); +void gik_im_client_set_cursor_location + (GikIMClient *client, + GdkRectangle *area); +gboolean gik_im_client_is_enabled (GikIMClient *client); + + +G_END_DECLS +#endif + diff --git a/gtk2/gikimcontext.c b/gtk2/gikimcontext.c new file mode 100644 index 0000000..5cce476 --- /dev/null +++ b/gtk2/gikimcontext.c @@ -0,0 +1,374 @@ +/* vim:set et ts=4: */ +/* GIK - The G Input Toolkit + * Copyright (C) 2008-2009 Huang Peng + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include "gikimcontext.h" +#include "gikimclient.h" + +/* define GOBJECT macros */ +#define GIK_TYPE_IM_CONTEXT \ + (_gik_type_im_context) +#define GIK_IM_CONTEXT(obj) \ + (GTK_CHECK_CAST ((obj), GIK_TYPE_IM_CONTEXT, GikIMContext)) +#define GIK_IM_CONTEXT_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST ((klass), GIK_TYPE_IM_CONTEXT, GikIMContextClass)) +#define GIK_IS_IM_CONTEXT(obj) \ + (GTK_CHECK_TYPE ((obj), GIK_TYPE_IM_CONTEXT)) +#define GIK_IS_IM_CONTEXT_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE ((klass), GIK_TYPE_IM_CONTEXT)) +#define GIK_IM_CONTEXT_GET_CLASS(obj) \ + (GTK_CHECK_GET_CLASS ((obj), GIK_TYPE_IM_CONTEXT, GikIMContextClass)) + +#define CURRENT_CONTEXT (gik_im_client_get_im_context (priv->client)) + +/* GikIMContextPriv */ +struct _GikIMContextPrivate { + GtkIMContext *slave; + GikIMClient *client; + GdkWindow *client_window; +}; + + +/* functions prototype */ +static void gik_im_context_class_init (GikIMContextClass *klass); +static void gik_im_context_init (GikIMContext *obj); +static void gik_im_context_finalize (GObject *obj); +static void gik_im_context_reset (GtkIMContext *context); +static gboolean gik_im_context_filter_keypress + (GtkIMContext *context, + GdkEventKey *key); +static void gik_im_context_focus_in (GtkIMContext *context); +static void gik_im_context_focus_out (GtkIMContext *context); +static void gik_im_context_get_preedit_string + (GtkIMContext *context, + gchar **str, + PangoAttrList **attrs, + gint *cursor_pos); +static void gik_im_context_set_client_window + (GtkIMContext *context, + GdkWindow *client); +static void gik_im_context_set_cursor_location + (GtkIMContext *context, + GdkRectangle *area); + +/* callback functions for slave context */ +static void _slave_commit_cb (GtkIMContext *slave, + gchar *string, + GikIMContext *context); +static void _slave_preedit_changed_cb (GtkIMContext *slave, + GikIMContext *context); +static void _slave_preedit_start_cb (GtkIMContext *slave, + GikIMContext *context); +static void _slave_preedit_end_cb (GtkIMContext *slave, + GikIMContext *context); +static void _slave_retrieve_surrounding_cb + (GtkIMContext *slave, + GikIMContext *context); +static void _slave_delete_surrounding_cb + (GtkIMContext *slave, + gint arg1, + gint arg2, + GikIMContext *context); + + + +static GType _gik_type_im_context = 0; +static GtkIMContextClass *parent_class = NULL; + +void +gik_im_context_register_type (GTypeModule *type_module) +{ + static const GTypeInfo gik_im_context_info = { + sizeof (GikIMContextClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gik_im_context_class_init, + NULL, /* class finialize */ + NULL, /* class data */ + sizeof (GikIMContext), + 0, + (GInstanceInitFunc) gik_im_context_init, + }; + + if (! _gik_type_im_context ) { + _gik_type_im_context = + g_type_module_register_type (type_module, + GTK_TYPE_IM_CONTEXT, + "GikIMContext", + &gik_im_context_info, + (GTypeFlags)0); + } +} + +GtkIMContext * +gik_im_context_new (void) +{ + GikIMContext *obj; + + obj = GIK_IM_CONTEXT(g_object_new (GIK_TYPE_IM_CONTEXT, NULL)); + + return GTK_IM_CONTEXT(obj); +} + +static void +gik_im_context_class_init (GikIMContextClass *klass) +{ + GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + parent_class = (GtkIMContextClass *) g_type_class_peek_parent (klass); + + g_type_class_add_private (klass, sizeof (GikIMContextPrivate)); + + im_context_class->reset = &gik_im_context_reset; + im_context_class->focus_in = &gik_im_context_focus_in; + im_context_class->focus_out = &gik_im_context_focus_out; + im_context_class->filter_keypress = &gik_im_context_filter_keypress; + im_context_class->get_preedit_string = &gik_im_context_get_preedit_string; + im_context_class->set_client_window = &gik_im_context_set_client_window; + im_context_class->set_cursor_location = &gik_im_context_set_cursor_location; + gobject_class->finalize = &gik_im_context_finalize; +} + +static void +gik_im_context_init (GikIMContext *obj) +{ + DEBUG_FUNCTION_IN; + + GError *error; + GikIMContext *gik = GIK_IM_CONTEXT (obj); + gik->priv = G_TYPE_INSTANCE_GET_PRIVATE (gik, GIK_TYPE_IM_CONTEXT, GikIMContextPrivate); + + gik->priv->client = gik_im_client_get_client (); + gik->priv->client_window = NULL; + + // Create slave im context + gik->priv->slave = gtk_im_context_simple_new (); + g_signal_connect (gik->priv->slave, + "commit", G_CALLBACK (_slave_commit_cb), obj); + g_signal_connect (gik->priv->slave, + "preedit-start", G_CALLBACK (_slave_preedit_start_cb), obj); + g_signal_connect (gik->priv->slave, + "preedit-end", G_CALLBACK (_slave_preedit_end_cb), obj); + g_signal_connect (gik->priv->slave, + "preedit-changed", G_CALLBACK (_slave_preedit_changed_cb), obj); + g_signal_connect (gik->priv->slave, + "retrieve-surrounding", G_CALLBACK (_slave_retrieve_surrounding_cb), obj); + g_signal_connect (gik->priv->slave, + "delete-surrounding", G_CALLBACK (_slave_delete_surrounding_cb), obj); +} + +static void +gik_im_context_finalize (GObject *obj) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (obj); + GikIMContextPrivate *priv = gik->priv; + + if (GTK_IM_CONTEXT (gik) == CURRENT_CONTEXT) { + gik_im_client_focus_out (priv->client); + gik_im_client_set_im_context (priv->client, NULL); + } + + g_object_unref (priv->slave); + g_object_unref (priv->client); + + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +static gboolean +gik_im_context_filter_keypress (GtkIMContext *context, + GdkEventKey *event) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + + if (context != CURRENT_CONTEXT) + return FALSE; + + if (gik_im_client_filter_keypress (priv->client, event)) + return TRUE; + return gtk_im_context_filter_keypress (priv->slave, event); +} + +static void +gik_im_context_focus_in (GtkIMContext *context) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + + if (context != CURRENT_CONTEXT) { + gik_im_client_focus_out (priv->client); + gik_im_client_set_im_context (priv->client, context); + } + + gik_im_client_focus_in (priv->client); + gtk_im_context_focus_in (priv->slave); +} + +static void +gik_im_context_focus_out (GtkIMContext *context) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + + gik_im_client_focus_out (priv->client); + gtk_im_context_focus_out (priv->slave); +} + +static void +gik_im_context_reset (GtkIMContext *context) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + + if (context == CURRENT_CONTEXT) { + gik_im_client_reset (priv->client); + } + gtk_im_context_reset (priv->slave); +} + + +static void +gik_im_context_get_preedit_string (GtkIMContext *context, + gchar **str, + PangoAttrList **attrs, + gint *cursor_pos) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + + if (context == CURRENT_CONTEXT && + gik_im_client_is_enabled (priv->client) && + gik_im_client_get_preedit_string (priv->client, str, attrs, cursor_pos)) { + return; + } + gtk_im_context_get_preedit_string (gik->priv->slave, str, attrs, cursor_pos); +} + + +static void +gik_im_context_set_client_window (GtkIMContext *context, GdkWindow *client) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + priv->client_window = client; + gtk_im_context_set_client_window (gik->priv->slave, client); +} + +static void +gik_im_context_set_cursor_location (GtkIMContext *context, GdkRectangle *area) +{ + DEBUG_FUNCTION_IN; + + GikIMContext *gik = GIK_IM_CONTEXT (context); + GikIMContextPrivate *priv = gik->priv; + if (context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) { + /* It is the focused context */ + gint x, y; + gdk_window_get_origin (priv->client_window, &x, &y); + area->x += x; + area->y += y; + gik_im_client_set_cursor_location (priv->client, area); + } + gtk_im_context_set_cursor_location (priv->slave, area); +} + +/* Callback functions for slave context */ +static void +_slave_commit_cb (GtkIMContext *slave, gchar *string, GikIMContext *context) +{ + DEBUG_FUNCTION_IN; + + GikIMContextPrivate *priv = context->priv; +#if 0 + if ((GtkIMContext *)context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) + return; +#endif + g_signal_emit_by_name (context, "commit", string); +} + +static void +_slave_preedit_changed_cb (GtkIMContext *slave, GikIMContext *context) +{ + DEBUG_FUNCTION_IN; + GikIMContextPrivate *priv = context->priv; + if ((GtkIMContext *)context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) + return; + g_signal_emit_by_name (context, "preedit-changed"); +} + +static void +_slave_preedit_start_cb (GtkIMContext *slave, GikIMContext *context) +{ + DEBUG_FUNCTION_IN; + GikIMContextPrivate *priv = context->priv; + if ((GtkIMContext *)context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) + return; + g_signal_emit_by_name (context, "preedit-start"); +} + +static void +_slave_preedit_end_cb (GtkIMContext *slave, GikIMContext *context) +{ + DEBUG_FUNCTION_IN; + GikIMContextPrivate *priv = context->priv; + if ((GtkIMContext *)context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) + return; + g_signal_emit_by_name (context, "preedit-end"); +} + +static void +_slave_retrieve_surrounding_cb (GtkIMContext *slave, GikIMContext *context) +{ + DEBUG_FUNCTION_IN; + GikIMContextPrivate *priv = context->priv; + if ((GtkIMContext *)context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) + return; + g_signal_emit_by_name (context, "retrieve-surrounding"); +} + +static void +_slave_delete_surrounding_cb (GtkIMContext *slave, gint a1, gint a2, GikIMContext *context) +{ + DEBUG_FUNCTION_IN; + GikIMContextPrivate *priv = context->priv; + if ((GtkIMContext *)context == CURRENT_CONTEXT && gik_im_client_is_enabled (priv->client)) + return; + g_signal_emit_by_name (context, "delete-surrounding", a1, a2); +} + diff --git a/gtk2/gikimcontext.h b/gtk2/gikimcontext.h new file mode 100644 index 0000000..39f8e4e --- /dev/null +++ b/gtk2/gikimcontext.h @@ -0,0 +1,51 @@ +/* vim:set et ts=4: */ +/* GIK - The G Input Toolkit + * Copyright (C) 2008-2009 Huang Peng + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GIK_IM_CONTEXT_H_ +#define __GIK_IM_CONTEXT_H_ + +#include <gtk/gtk.h> + +/* + * Type macros. + */ + +G_BEGIN_DECLS +typedef struct _GikIMContext GikIMContext; +typedef struct _GikIMContextClass GikIMContextClass; +typedef struct _GikIMContextPrivate GikIMContextPrivate; + +struct _GikIMContext { + GtkIMContext parent; + /* instance members */ + GikIMContextPrivate *priv; +}; + +struct _GikIMContextClass { + GtkIMContextClass parent; + /* class members */ +}; + +GtkIMContext *gik_im_context_new (void); +void gik_im_context_register_type (GTypeModule *type_module); +void gik_im_context_shutdown (void); + +G_END_DECLS +#endif + diff --git a/ibus/Makefile b/ibus/Makefile new file mode 100644 index 0000000..f24037e --- /dev/null +++ b/ibus/Makefile @@ -0,0 +1,6 @@ +PYTHON = env PYTHONPATH=../ python +test-server: + $(PYTHON) ./ibusdaemon.py + +test-engine: + $(PYTHON) ./eng_demo.py diff --git a/ibus/__init__.py b/ibus/__init__.py new file mode 100644 index 0000000..8832e1e --- /dev/null +++ b/ibus/__init__.py @@ -0,0 +1,11 @@ +from object import * +from attribute import * +from common import * +from connection import * +from interface import * +from bus import * +from client import * +from engine import * +from factorymanager import * +from clientmanager import * +from exception import * diff --git a/ibus/attribute.py b/ibus/attribute.py new file mode 100644 index 0000000..437bc2a --- /dev/null +++ b/ibus/attribute.py @@ -0,0 +1,58 @@ + +import dbus + +ATTR_TYPE_DECORATION = 1 +ATTR_TYPE_FOREGROUND = 2 +ATTR_TYPE_BACKGROUND = 3 + +ATTR_DECORATION_NONE = 0 +ATTR_DECORATION_UNDERLINE = 1 +ATTR_DECORATION_HIGHLIGHT = 2 +ATTR_DECORATION_REVERSE = 4 + +class Attribute: + def __init__ (self, type, value, start_index, end_index): + self._type = type + self._start_index = start_index + self._end_index = end_index + self._value = value + + def get_values (self): + return [dbus.UInt32 (self._type), + dbus.UInt32 (self._value), + dbus.UInt32 (self._start_index), + dbus.UInt32 (self._end_index)] + +class AttributeDecoration (Attribute): + def __init__(self, value, start_index, end_index): + Attribute.__init__ (self, ATTR_TYPE_DECORATION, value, start_index, end_index) + + +class AttributeForeground (Attribute): + def __init__(self, value, start_index, end_index): + Attribute.__init__ (self, ATTR_TYPE_FOREGROUND, value, start_index, end_index) + +class AttributeBackground (Attribute): + def __init__(self, value, start_index, end_index): + Attribute.__init__ (self, ATTR_TYPE_BACKGROUND, value, start_index, end_index) + +def ARGB (a, r, g, b): + return ((a & 0xff)<<24) + ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff) + +def RGB (r, g, b): + return ARGB (255, r, g, b) + +class AttrList: + def __init__ (self, attrs = []): + self._attrs = [] + for attr in attrs: + self.append (attr) + + def append (self, attr): + assert isinstance (attr, Attribute) + self._attrs.append (attr) + + def get_array (self): + get_values = lambda attr: attr.get_values () + return map (get_values, self._attrs) + diff --git a/ibus/bus.py b/ibus/bus.py new file mode 100755 index 0000000..01e5e73 --- /dev/null +++ b/ibus/bus.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +import weakref +import dbus +import ibus +from ibus import keysyms + +class IBus (ibus.Object): + def __init__ (self): + ibus.Object.__init__ (self) + self._connections = {} + self._client_manager = ibus.ClientManager () + self._factory_manager = ibus.FactoryManager () + + self._focused_client = None + self._last_focused_client = None + + self._last_key = None + + def new_connection (self, dbusconn): + assert dbusconn not in self._connections + self._connections[dbusconn] = ibus.Connection (dbusconn) + + def remove_connection (self, dbusconn): + assert dbusconn in self._connections + + # destroy the connection + self._connections[dbusconn].destroy () + del self._connections[dbusconn] + + def _lookup_ibus_connection (self, dbusconn): + if dbusconn not in self._connections: + raise ibus.IBusException ("can not find ibus.Connection") + return self._connections[dbusconn] + + ########################################################## + # methods for im client + ########################################################## + def register_client (self, name, dbusconn): + ibusconn = self._lookup_ibus_connection (dbusconn) + client = self._client_manager.register_client (name, ibusconn) + factory = self._factory_manager.get_default_factory () + client.set_engine_factory (factory) + + def focus_in (self, dbusconn): + client = self._lookup_client (dbusconn) + + if self._focused_client != client and self._focused_client != None: + self._focused_client.focus_out () + + self._focused_client = client + self._last_focused_client = client + client.focus_in () + + def focus_out (self, dbusconn): + client = self._lookup_client (dbusconn) + if client == self._focused_client: + self._focused_client = None + client.focus_out () + + def reset (self, dbusconn): + client = self._lookup_client (dbusconn) + client.reset () + + def is_enabled (self, dbusconn): + client = self._lookup_client (dbusconn) + return client.is_enabled () + + def process_key_event (self, keyval, is_press, state, + dbusconn, reply_cb, error_cb): + client = self._lookup_client (dbusconn) + + if self._filter_hotkeys (client, keyval, is_press, state): + reply_cb (True) + return + else: + client.process_key_event (keyval, is_press, state, reply_cb, error_cb) + + def set_cursor_location (self, x, y, w, h, dbusconn): + client = self._lookup_client (dbusconn) + client.set_cursor_location (x, y, w, h) + + def _filter_hotkeys (self, client, keyval, is_press, state): + if is_press and keyval == keysyms.space \ + and state == keysyms.CONTROL_MASK: + enable = not client.is_enabled () + client.set_enable (enable) + if client.get_engine_factory () == None and enable: + factory = self._factory_manager.get_default_factory() + client.set_engine_factory (factory) + return True + return False + + def _lookup_client (self, dbusconn): + ibusconn = self._lookup_ibus_connection (dbusconn) + return self._client_manager.lookup_client (ibusconn) + if dbusconn not in self._clients: + raise ibus.IBusException ("not register the client") + return self._clients[dbusconn] + + ########################################################## + # methods for im client + ########################################################## + def register_factories (self, object_paths, dbusconn): + ibusconn = self._lookup_ibus_connection (dbusconn) + self._factory_manager.register_factories (object_paths, ibusconn) + + def dispatch_dbus_signal (self, dbusconn, message): + ibusconn = self._lookup_ibus_connection (dbusconn) + ibusconn.dispatch_dbus_signal (message) + + def _lookup_engine (self, dbusconn, path): + ibusconn = self._lookup_ibus_connection (dbusconn) + return self._factory_manager.lookup_engine (ibusconn, path) + + + + +class IBusProxy (ibus.IIBus): + SUPPORTS_MULTIPLE_CONNECTIONS = True + + def __init__ (self): + ibus.IIBus.__init__ (self) + self._ibus = IBus () + + def new_connection (self, dbusconn): + self._ibus.new_connection (dbusconn) + + def remove_connection (self, dbusconn): + self._ibus.remove_connection (dbusconn) + + def dispatch_dbus_signal (self, dbusconn, message): + return self._ibus.dispatch_dbus_signal (dbusconn, message) + + def GetIBusAddress (self, dbusconn): + return self._ibus_addr + + def RegisterClient (self, client_name, dbusconn): + self._ibus.register_client (client_name, dbusconn) + + def RegisterFactories (self, object_paths, dbusconn): + self._ibus.register_factories (object_paths, dbusconn) + + def UnregisterEngines (self, object_paths, dbusconn): + self._ibus.unregister_engines (object_paths, dbusconn) + + def ProcessKeyEvent (self, keyval, is_press, state, \ + dbusconn, reply_cb, error_cb): + try: + self._ibus.process_key_event (keyval, is_press, state, + dbusconn, reply_cb, error_cb) + except Exception, e: + error_cb (e) + + def SetCursorLocation (self, x, y, w, h, dbusconn): + self._ibus.set_cursor_location (x, y, w, h, dbusconn) + + def FocusIn (self, dbusconn): + self._ibus.focus_in (dbusconn) + + def FocusOut (self, dbusconn): + self._ibus.focus_out (dbusconn) + + def Reset (self, dbusconn): + self._ibus.reset (dbusconn) + + def IsEnabled (self, dbusconn): + return self._ibus.is_enabled (dbusconn) + diff --git a/ibus/client.py b/ibus/client.py new file mode 100644 index 0000000..21e7eac --- /dev/null +++ b/ibus/client.py @@ -0,0 +1,107 @@ +import ibus + +class Client (ibus.Object): + def __init__ (self, name, ibusconn): + ibus.Object.__init__ (self) + + self._ibusconn = ibusconn + self._ibusconn.connect ("destroy", self._ibusconn_destroy_cb) + + # init default values + self._enable = False + self._factory = None + self._engine = None + self._engine_handler_ids = [] + + def process_key_event (self, keyval, is_press, state, + reply_cb, error_cb): + if self._engine != None and self._enable: + self._engine.process_key_event (keyval, is_press, state, + reply_cb, error_cb) + else: + reply_cb (False) + + def set_cursor_location (self, x, y, w, h): + if self._engine: + self._engine.set_cursor_location (x, y, w, h) + + def focus_in (self): + if self._engine: + self._engine.focus_in () + + def focus_out (self): + if self._engine: + self._engine.focus_out () + + def reset (self): + if self._engine: + self._engine.reset () + + def is_enabled (self): + return self._enable + + def set_enable (self, enable): + if self._enable != enable: + self._enable = enable + if self._enable: + self._ibusconn.emit_dbus_signal ("Enabled") + else: + self._ibusconn.emit_dbus_signal ("Disabled") + if self._engine: + self._engine.set_enable (self._enable) + + def commit_string (self, text): + self._ibusconn.emit_dbus_signal ("CommitString", text) + + def preedit_changed (self, text, attrs, cursor): + self._ibusconn.emit_dbus_signal ("PreeditChanged", text, attrs.get_array (), cursor) + + def set_engine_factory (self, factory): + if self._factory == factory: + return + + if self._engine != None: + self._remove_engine_handlers () + self._engine.destroy () + self._engine = None + + self._factory = factory + + if self._factory: + self._engine = self._factory.create_engine () + self._install_engine_handlers () + + def get_engine_factory (self): + return self._factory + + def _engine_destroy_cb (self, engine): + if self._engine == engine: + self._remove_engine_handlers () + self._engine = None + self._factory = None + + def _ibusconn_destroy_cb (self, ibusconn): + self._factory = None + if self._engine != None: + self._remove_engine_handlers () + self._engine.destroy () + self._engine = None + + def _commit_string_cb (self, engine, text): + self.commit_string (text) + + def _preedit_changed_cb (self, engine, text, attrs, cursor): + self.preedit_changed (self, text, attrs, cursor) + + def _remove_engine_handlers (self): + assert self._engine != None + for id in self._engine_handler_ids: + self._engine.disconnect (id) + self._engine_handler_ids = [] + + def _install_engine_handlers (self): + id = self._engine.connect ("destroy", self._engine_destroy_cb) + id = self._engine.connect ("commit-string", self._commit_string_cb) + self._engine_handler_ids.append (id) + id = self._engine.connect ("preedit-changed", self._preedit_changed_cb) + self._engine_handler_ids.append (id) diff --git a/ibus/clientmanager.py b/ibus/clientmanager.py new file mode 100644 index 0000000..92030da --- /dev/null +++ b/ibus/clientmanager.py @@ -0,0 +1,20 @@ +import ibus + +class ClientManager (ibus.Object): + def __init__ (self): + self._clients = {} + + def register_client (self, name, ibusconn): + if ibusconn in self._clients: + raise ibus.IBusException ("client has been registered") + client = ibus.Client (name, ibusconn) + self._clients[ibusconn] = client + ibusconn.connect ("destroy", self._ibusconn_destroy_cb) + return client + + def lookup_client (self, ibusconn): + return self._clients[ibusconn] + + def _ibusconn_destroy_cb (self, ibusconn): + del self._clients[ibusconn] + diff --git a/ibus/common.py b/ibus/common.py new file mode 100644 index 0000000..76eab3e --- /dev/null +++ b/ibus/common.py @@ -0,0 +1,20 @@ +IBUS_ADDR = "unix:abstract=/tmp/ibus" +# IBUS_ADDR = "tcp:host=localhost,port=7799" + +IBUS_IFACE = "org.freedesktop.IBus" +IBUS_PATH = "/org/freedesktop/IBus" +IBUS_NAME = "org.freedesktop.IBus" + +IBUS_ENGINE_FACTORY_IFACE = "org.freedesktop.IBus.EngineFactory" +IBUS_ENGINE_IFACE = "org.freedesktop.IBus.Engine" + +def default_reply_handler ( *args): + pass + +def default_error_handler (e): + print e + +DEFAULT_ASYNC_HANDLERS = { + "reply_handler" : default_reply_handler, + "error_handler" : default_error_handler +} diff --git a/ibus/connection.py b/ibus/connection.py new file mode 100644 index 0000000..f1d2882 --- /dev/null +++ b/ibus/connection.py @@ -0,0 +1,32 @@ +import dbus.lowlevel +import ibus +import gobject + +class Connection (ibus.Object): + __gsignals__ = { + "dbus-signal" : ( + gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT, ) + ) + } + def __init__ (self, dbusconn): + ibus.Object.__init__ (self) + self._dbusconn = dbusconn + + def get_object (self, path): + return self._dbusconn.get_object ("no.name", path) + + def emit_dbus_signal (self, name, *args): + message = dbus.lowlevel.SignalMessage (ibus.IBUS_PATH, ibus.IBUS_IFACE, name) + message.append (*args) + self._dbusconn.send_message (message) + self._dbusconn.flush () + + def do_destroy (self): + self._dbusconn = None + + def dispatch_dbus_signal (self, message): + self.emit ("dbus-signal", message) + +gobject.type_register (Connection) diff --git a/ibus/driver.py b/ibus/driver.py new file mode 100755 index 0000000..8b22e2a --- /dev/null +++ b/ibus/driver.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +import dbus +import dbus.server +import dbus.lowlevel +import dbus.service +import dbus.mainloop.glib +import gobject + +class DBusObject (dbus.service.Object): + SUPPORTS_MULTIPLE_CONNECTIONS = True + def __init__ (self): + dbus.service.Object.__init__ (self) + self._max_id = 1 + + @dbus.service.method (dbus_interface=dbus.BUS_DAEMON_IFACE, out_signature="s", connection_keyword="connection") + def Hello (self, connection): + print "Hello is called" + name = "ibus.%d" % self._max_id + self._max_id = self._max_id +1 + connection.set_unique_name (name) + return name + + diff --git a/ibus/eng_demo.py b/ibus/eng_demo.py new file mode 100755 index 0000000..f599394 --- /dev/null +++ b/ibus/eng_demo.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +import interface +import gobject +import gtk +import dbus.connection +import dbus.mainloop.glib +import ibus.keysyms as keysyms +from ibus.common import \ + IBUS_NAME, \ + IBUS_IFACE, \ + IBUS_PATH, \ + IBUS_ADDR + +IBUS_DEMO_ENGINE_FACTORY_PATH = "/com/redhat/IBus/engines/Demo/Factory" +IBUS_DEMO_ENGINE_PATH = "/com/redhat/IBus/engines/Demo/Engine/%d" + +class LookupTable (gtk.Window): + def __init__ (self): + gtk.Window.__init__ (self, gtk.WINDOW_POPUP) + self._enable = False + # create ui + vbox = gtk.VBox () + self._preedit_label = gtk.Label ("preedit string") + self._candidates_label = gtk.Label ("candidates") + vbox.pack_start (self._preedit_label) + vbox.pack_start (self._candidates_label) + self.add (vbox) + + def focus_in (self): + if self._enable: + self.show_all () + + def focus_out (self): + if self._enable: + self.hide_all () + + def set_enable (self, enable): + self._enable = enable + if enable: + self.show_all () + else: + self.hide_all () + + def set_cursor_location (self, x, y, w, h): + self.move (x + w, y + h) + +class DemoEngineFactory (interface.IEngineFactory): + NAME = "DemoEngine" + LANG = "en" + ICON = "" + AUTHORS = "Huang Peng <shawn.p.huang@gmail.com>" + CREDITS = "GPLv2" + + def __init__ (self, dbusconn): + interface.IEngineFactory.__init__ (self, dbusconn, object_path = IBUS_DEMO_ENGINE_FACTORY_PATH) + self._dbusconn = dbusconn + self._max_engine_id = 1 + + def GetInfo (self): + return [ + self.NAME, + self.LANG, + self.ICON, + self.AUTHORS, + self.CREDITS + ] + + def CreateEngine (self): + engine_path = IBUS_DEMO_ENGINE_PATH % self._max_engine_id + self._max_engine_id += 1 + return DemoEngine (self._dbusconn, engine_path) + + +class DemoEngine (interface.IEngine): + + def __init__ (self, dbusconn, object_path): + interface.IEngine.__init__ (self, dbusconn, object_path = object_path) + self._dbusconn = dbusconn + self._lookup_table = LookupTable () + + def ProcessKeyEvent (self, keyval, is_press, state): + if not is_press: + return False + if keyval < 128 and (state & keysyms.CONTROL_MASK | keysyms.MOD1_MASK | keysyms.SHIFT_MASK) == 0: + self.CommitString (chr(keyval)) + print "commit %s" % chr(keyval) + return True + return False + + def FocusIn (self): + print "FocusIn" + self._lookup_table.focus_in () + + def FocusOut (self): + print "FocusOut" + self._lookup_table.focus_out () + + def Reset (self): + print "Reset" + + def SetEnable (self, enable): + self._enable = enable + self._lookup_table.set_enable (enable) + + def Destroy (self): + print "Destroy" + self._lookup_table.destroy () + + def SetCursorLocation (self, x, y, w, h): + self._lookup_table.set_cursor_location (x, y, w, h) + + +class IMApp: + def __init__ (self): + self._dbusconn = dbus.connection.Connection (IBUS_ADDR) + self._dbusconn.add_signal_receiver (self._disconnected_cb, + "Disconnected", + dbus_interface=dbus.LOCAL_IFACE) + self._engine = DemoEngineFactory (self._dbusconn) + self._ibus = self._dbusconn.get_object (IBUS_NAME, IBUS_PATH) + self._ibus.RegisterFactories ([IBUS_DEMO_ENGINE_FACTORY_PATH]) + + def run (self): + gtk.main () + + def _disconnected_cb (self): + print "disconnected" + gtk.main_quit () + + +def main (): + IMApp ().run () + +if __name__ == "__main__": + dbus.mainloop.glib.DBusGMainLoop (set_as_default=True) + main () diff --git a/ibus/engine.py b/ibus/engine.py new file mode 100644 index 0000000..ec55a17 --- /dev/null +++ b/ibus/engine.py @@ -0,0 +1,123 @@ +import weakref +import gobject +import ibus + +class EngineFactory (ibus.Object): + def __init__ (self, ibusconn, object_path): + ibus.Object.__init__ (self) + self._ibusconn = ibusconn + self._object_path = object_path + self._factory = self._ibusconn.get_object (self._object_path) + + self._ibusconn.connect ("destroy", self._ibusconn_destroy_cb) + + self._ibusconn.connect ("dbus-signal", self._dbus_signal_cb) + self._engines = weakref.WeakValueDictionary () + + self._info = None + + def get_object_path (self): + return self._object_path + + def get_info (self): + if self._info == None: + self._info = self._factory.GetInfo () + return self._info + + def create_engine (self): + object_path = self._factory.CreateEngine () + engine = Engine (self._ibusconn, object_path) + self._engines[object_path] = engine + return engine + + def destroy (self): + ibus.Object.destroy (self) + self._ibusconn = None + self._factory = None + + def _ibusconn_destroy_cb (self, ibusconn): + self.destroy () + + def _dbus_signal_cb (self, ibusconn, message): + object_path = message.get_path () + if object_path in self._engines: + self._engines[object_path].handle_dbus_signal (message) + + # methods for cmp + def __lt__ (self, other): + x = self.get_info () + y = other.get_info () + if x[1] < y[1]: return True + if x[1] == y[1]: return x[0] < y[0] + + def __gt__ (self, other): + x = self.get_info () + y = other.get_info () + if x[1] > y[1]: return True + if x[1] == y[1]: return x[0] > y[0] + + +class Engine (ibus.Object): + __gsignals__ = { + "commit-string" : ( + gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, )), + "preedit-changed" : ( + gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT, gobject.TYPE_UINT)) + } + + def __init__ (self, ibusconn, object_path): + ibus.Object.__init__ (self) + self._ibusconn = ibusconn + self._object_path = object_path + self._engine = ibusconn.get_object (self._object_path) + self._ibusconn.connect ("destroy", self._ibusconn_destroy_cb) + + def handle_dbus_signal (self, message): + if message.is_signal (ibus.IBUS_ENGINE_IFACE, "CommitString"): + args = message.get_args_list () + self.emit ("commit-string", args[0]) + return True + elif message.is_signal (ibus.IBUS_ENGINE_IFACE, "PreeditChanged"): + args = message.get_args_list () + self.emit ("preedit-changed", args[0], args[1], args[2]) + return True + else: + return False + + def focus_in (self): + self._engine.FocusIn (**ibus.DEFAULT_ASYNC_HANDLERS) + + def focus_out (self): + self._engine.FocusOut (**ibus.DEFAULT_ASYNC_HANDLERS) + + def reset (self): + self._engine.Reset (**ibus.DEFAULT_ASYNC_HANDLERS) + + def process_key_event (self, keyval, is_press, state, reply_cb, error_cb): + self._engine.ProcessKeyEvent (keyval, is_press, state, + reply_handler = reply_cb, + error_handler = error_cb) + + def set_cursor_location (self, x, y, w, h): + self._engine.SetCursorLocation (x, y, w, h) + + def set_enable (self, enable): + self._engine.SetEnable (enable) + + def destroy (self): + ibus.Object.destroy (self) + if self._engine: + self._engine.Destroy () + self._engine = None + self._ibusconn = None + + def _ibusconn_destroy_cb (self, ibusconn): + self._engine = None + self.destroy () + +gobject.type_register (Engine) + diff --git a/ibus/exception.py b/ibus/exception.py new file mode 100644 index 0000000..c699209 --- /dev/null +++ b/ibus/exception.py @@ -0,0 +1,2 @@ +class IBusException (Exception): + pass diff --git a/ibus/factorymanager.py b/ibus/factorymanager.py new file mode 100644 index 0000000..ffc7885 --- /dev/null +++ b/ibus/factorymanager.py @@ -0,0 +1,76 @@ +import weakref +import gobject +import ibus + +class FactoryManager (ibus.Object): + __gsignals__ = { + 'new-factories-added' : ( + gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT, ) + ) + } + def __init__ (self): + ibus.Object.__init__ (self) + self._factories = {} + self._ibusconn_factory_dict = {} + self._default_factory = None + self._sorted_factories = None + + def register_factories (self, object_paths, ibusconn): + if ibusconn in self._factories: + raise ibus.IBusException ("this conn has registered factories!") + + self._ibusconn_factory_dict[ibusconn] = [] + + for object_path in object_paths: + if object_path in self._factories: + raise ibus.IBusException ( + "Factory [%s] has been registered!" % object_path) + + factory = ibus.EngineFactory (ibusconn, object_path) + self._factories[object_path] = factory + self._ibusconn_factory_dict[ibusconn].append (object_path) + + ibusconn.connect ("destroy", self._ibusconn_destroy_cb) + + self.emit ("new-factories-added", + self._ibusconn_factory_dict[ibusconn][:]) + + def get_default_factory (self): + if self._default_factory == None: + factories = self._get_sorted_factories () + if factories: + self._default_factory = factories[0] + + return self._default_factory + + def get_next_factory (self, factory): + factories = self._get_sorted_factories () + i = factories.index (factory) + 1 + if i >= len (factories): + i = 0 + + return factories[i] + + def _get_sorted_factories (self, resort = False): + if not self._sorted_factories or resort: + factories = self._factories.values () + factories.sort () + self._sorted_factories = factories + return self._sorted_factories + + def _ibusconn_destroy_cb (self, ibusconn): + assert ibusconn in self._ibusconn_factory_dict + + for object_path in self._ibusconn_factory_dict[ibusconn]: + factory = self._factories[object_path] + if factory == self._default_factory: + self._default_factory = None + del self._factories[object_path] + + del self._ibusconn_factory_dict[ibusconn] + self._sorted_factories = None + +gobject.type_register (FactoryManager) + diff --git a/ibus/ibusdaemon.py b/ibus/ibusdaemon.py new file mode 100755 index 0000000..5093562 --- /dev/null +++ b/ibus/ibusdaemon.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +import sys +import gobject +import dbus.server +import dbus.lowlevel +import dbus.mainloop.glib +import ibus + +class IBusServer (dbus.server.Server): + def __init__ (self): + dbus.server.Server.__init__ (self, ibus.IBUS_ADDR) + self._ibus = ibus.IBusProxy () + + self.register_object (self._ibus, ibus.IBUS_PATH) + + def new_connection (self, server, dbusconn): + dbusconn.add_message_filter (self.message_filter_cb) + self._ibus.new_connection (dbusconn) + + def remove_connection (self, dbusconn): + self._ibus.remove_connection (dbusconn) + + def message_filter_cb (self, dbusconn, message): + if message.is_method_call (dbus.LOCAL_IFACE, "Disconnected"): + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + + if message.get_type () == 4: # is signal + if self._ibus.dispatch_dbus_signal (dbusconn, message): + return dbus.lowlevel.HANDLER_RESULT_HANDLED + + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + + def _print_message (self, message): + print "Got a Message (%s) : " % message.__class__.__name__ + print "\t From: %s" % message.get_sender () + print "\t To: %s" % message.get_destination () + print "\t Interface: %s" % message.get_interface () + print "\t Path: %s" % message.get_path () + print "\t Member: %s" % message.get_member () + print "\t Arguments:" + i = 0 + for arg in message.get_args_list(): + print "\t\t Arg[%d] : %s" % (i, arg) + i = i + 1 + +if __name__ == "__main__": + dbus.mainloop.glib.DBusGMainLoop (set_as_default = True) + loop = gobject.MainLoop () + bus = IBusServer () + print "IBUS_ADDRESS=\"%s\"" % bus.get_address () + try: + loop.run () + except KeyboardInterrupt, e: + print "daemon exits" + sys.exit () + diff --git a/ibus/interface.py b/ibus/interface.py new file mode 100644 index 0000000..52a7028 --- /dev/null +++ b/ibus/interface.py @@ -0,0 +1,124 @@ +import dbus.service +from common import \ + IBUS_NAME, \ + IBUS_IFACE, \ + IBUS_PATH, \ + IBUS_ENGINE_IFACE, \ + IBUS_ENGINE_FACTORY_IFACE + +class IIBus (dbus.service.Object): + # define method decorator. + method = lambda **args: \ + dbus.service.method (dbus_interface=IBUS_IFACE, \ + connection_keyword="dbusconn", \ + **args) + + # define async method decorator. + async_method = lambda **args: \ + dbus.service.method (dbus_interface=IBUS_IFACE, \ + connection_keyword="dbusconn", \ + async_callbacks=("reply_cb", "error_cb"), \ + **args) + + @method (out_signature="s") + def GetIBusAddress (self, dbusconn): pass + + @method (in_signature="s") + def RegisterClient (self, client_name, dbusconn): pass + + @method () + def UnregisterClient (self, dbusconn): pass + + @method (in_signature="ao") + def RegisterFactories (self, object_paths, dbusconn): pass + + @method (in_signature="ao") + def UnregisterFactories (self, object_paths, dbusconn): pass + + @async_method (in_signature="ubu", out_signature="b") + def ProcessKeyEvent (self, keyval, is_press, state, dbusconn, reply_cb, error_cb): pass + + @method (in_signature="iiii") + def SetCursorLocation (self, x, y, w, h, dbusconn): pass + + @method () + def FocusIn (self, dbusconn): pass + + @method () + def FocusOut (self, dbusconn): pass + + @method () + def Reset (self, dbusconn): pass + + @method (out_signature="b") + def IsEnabled (self, dbusconn): pass + +class IEngineFactory (dbus.service.Object): + # define method decorator. + method = lambda **args: \ + dbus.service.method (dbus_interface=IBUS_ENGINE_FACTORY_IFACE, \ + **args) + + # define async method decorator. + async_method = lambda **args: \ + dbus.service.method (dbus_interface=IBUS_ENGINE_FACTORY_IFACE, \ + async_callbacks=("reply_cb", "error_cb"), \ + **args) + # Return a array. [name, language, icon_path, authors, credits] + @method (out_signature="as") + def GetInfo (self): pass + + # Factory should allocate all resources in this method + @method () + def Initialize (self): pass + + # Factory should free all allocated resources in this method + @method () + def Uninitialize (self): pass + + # Create an input context and return the id of the context. + # If failed, it will return "" or None. + @method (out_signature="o") + def CreateEngine (self): pass + +class IEngine (dbus.service.Object): + # define method decorator. + method = lambda **args: \ + dbus.service.method (dbus_interface=IBUS_ENGINE_IFACE, \ + **args) + + # define signal decorator. + signal = lambda **args: \ + dbus.service.signal (dbus_interface=IBUS_ENGINE_IFACE, \ + **args) + + # define async method decorator. + async_method = lambda **args: \ + dbus.service.method (dbus_interface=IBUS_ENGINE_IFACE, \ + async_callbacks=("reply_cb", "error_cb"), \ + **args) + + @method (in_signature="ubu", out_signature="b") + def ProcessKeyEvent (self, keyval, is_press, state): + pass + + @method (in_signature="iiii") + def SetCursorLocation (self, x, y, w, h): pass + + @method () + def FocusIn (self): pass + + @method () + def FocusOut (self): pass + + @method () + def Reset (self): pass + + @method (in_signature="b") + def SetEnable (self, enable): pass + + @method () + def Destroy (self): pass + + @signal () + def CommitString (self, text): pass diff --git a/ibus/keysyms.py b/ibus/keysyms.py new file mode 100644 index 0000000..e4678c2 --- /dev/null +++ b/ibus/keysyms.py @@ -0,0 +1,1521 @@ +# -*- Mode: Python; py-indent-offset: 4 -*- +# pygtk - Python bindings for the GTK toolkit. +# Copyright (C) 1998-2003 James Henstridge +# +# gtk/keysyms.py: list of keysyms. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +SHIFT_MASK = 1 << 0 +LOCK_MASK = 1 << 1 +CONTROL_MASK = 1 << 2 +MOD1_MASK = 1 << 3 +MOD2_MASK = 1 << 4 +MOD3_MASK = 1 << 5 +MOD4_MASK = 1 << 6 +MOD5_MASK = 1 << 7 +BUTTON1_MASK = 1 << 8 +BUTTON2_MASK = 1 << 9 +BUTTON3_MASK = 1 << 10 +BUTTON4_MASK = 1 << 11 +BUTTON5_MASK = 1 << 12 + +SUPER_MASK = 1 << 26 +HYPER_MASK = 1 << 27 +META_MASK = 1 << 28 + +RELEASE_MASK = 1 << 30 + +MODIFIER_MASK = 0x5c001fff + +VoidSymbol = 0xFFFFFF +BackSpace = 0xFF08 +Tab = 0xFF09 +Linefeed = 0xFF0A +Clear = 0xFF0B +Return = 0xFF0D +Pause = 0xFF13 +Scroll_Lock = 0xFF14 +Sys_Req = 0xFF15 +Escape = 0xFF1B +Delete = 0xFFFF +Multi_key = 0xFF20 +Codeinput = 0xFF37 +SingleCandidate = 0xFF3C +MultipleCandidate = 0xFF3D +PreviousCandidate = 0xFF3E +Kanji = 0xFF21 +Muhenkan = 0xFF22 +Henkan_Mode = 0xFF23 +Henkan = 0xFF23 +Romaji = 0xFF24 +Hiragana = 0xFF25 +Katakana = 0xFF26 +Hiragana_Katakana = 0xFF27 +Zenkaku = 0xFF28 +Hankaku = 0xFF29 +Zenkaku_Hankaku = 0xFF2A +Touroku = 0xFF2B +Massyo = 0xFF2C +Kana_Lock = 0xFF2D +Kana_Shift = 0xFF2E +Eisu_Shift = 0xFF2F +Eisu_toggle = 0xFF30 +Kanji_Bangou = 0xFF37 +Zen_Koho = 0xFF3D +Mae_Koho = 0xFF3E +Home = 0xFF50 +Left = 0xFF51 +Up = 0xFF52 +Right = 0xFF53 +Down = 0xFF54 +Prior = 0xFF55 +Page_Up = 0xFF55 +Next = 0xFF56 +Page_Down = 0xFF56 +End = 0xFF57 +Begin = 0xFF58 +Select = 0xFF60 +Print = 0xFF61 +Execute = 0xFF62 +Insert = 0xFF63 +Undo = 0xFF65 +Redo = 0xFF66 +Menu = 0xFF67 +Find = 0xFF68 +Cancel = 0xFF69 +Help = 0xFF6A +Break = 0xFF6B +Mode_switch = 0xFF7E +script_switch = 0xFF7E +Num_Lock = 0xFF7F +KP_Space = 0xFF80 +KP_Tab = 0xFF89 +KP_Enter = 0xFF8D +KP_F1 = 0xFF91 +KP_F2 = 0xFF92 +KP_F3 = 0xFF93 +KP_F4 = 0xFF94 +KP_Home = 0xFF95 +KP_Left = 0xFF96 +KP_Up = 0xFF97 +KP_Right = 0xFF98 +KP_Down = 0xFF99 +KP_Prior = 0xFF9A +KP_Page_Up = 0xFF9A +KP_Next = 0xFF9B +KP_Page_Down = 0xFF9B +KP_End = 0xFF9C +KP_Begin = 0xFF9D +KP_Insert = 0xFF9E +KP_Delete = 0xFF9F +KP_Equal = 0xFFBD +KP_Multiply = 0xFFAA +KP_Add = 0xFFAB +KP_Separator = 0xFFAC +KP_Subtract = 0xFFAD +KP_Decimal = 0xFFAE +KP_Divide = 0xFFAF +KP_0 = 0xFFB0 +KP_1 = 0xFFB1 +KP_2 = 0xFFB2 +KP_3 = 0xFFB3 +KP_4 = 0xFFB4 +KP_5 = 0xFFB5 +KP_6 = 0xFFB6 +KP_7 = 0xFFB7 +KP_8 = 0xFFB8 +KP_9 = 0xFFB9 +F1 = 0xFFBE +F2 = 0xFFBF +F3 = 0xFFC0 +F4 = 0xFFC1 +F5 = 0xFFC2 +F6 = 0xFFC3 +F7 = 0xFFC4 +F8 = 0xFFC5 +F9 = 0xFFC6 +F10 = 0xFFC7 +F11 = 0xFFC8 +L1 = 0xFFC8 +F12 = 0xFFC9 +L2 = 0xFFC9 +F13 = 0xFFCA +L3 = 0xFFCA +F14 = 0xFFCB +L4 = 0xFFCB +F15 = 0xFFCC +L5 = 0xFFCC +F16 = 0xFFCD +L6 = 0xFFCD +F17 = 0xFFCE +L7 = 0xFFCE +F18 = 0xFFCF +L8 = 0xFFCF +F19 = 0xFFD0 +L9 = 0xFFD0 +F20 = 0xFFD1 +L10 = 0xFFD1 +F21 = 0xFFD2 +R1 = 0xFFD2 +F22 = 0xFFD3 +R2 = 0xFFD3 +F23 = 0xFFD4 +R3 = 0xFFD4 +F24 = 0xFFD5 +R4 = 0xFFD5 +F25 = 0xFFD6 +R5 = 0xFFD6 +F26 = 0xFFD7 +R6 = 0xFFD7 +F27 = 0xFFD8 +R7 = 0xFFD8 +F28 = 0xFFD9 +R8 = 0xFFD9 +F29 = 0xFFDA +R9 = 0xFFDA +F30 = 0xFFDB +R10 = 0xFFDB +F31 = 0xFFDC +R11 = 0xFFDC +F32 = 0xFFDD +R12 = 0xFFDD +F33 = 0xFFDE +R13 = 0xFFDE +F34 = 0xFFDF +R14 = 0xFFDF +F35 = 0xFFE0 +R15 = 0xFFE0 +Shift_L = 0xFFE1 +Shift_R = 0xFFE2 +Control_L = 0xFFE3 +Control_R = 0xFFE4 +Caps_Lock = 0xFFE5 +Shift_Lock = 0xFFE6 +Meta_L = 0xFFE7 +Meta_R = 0xFFE8 +Alt_L = 0xFFE9 +Alt_R = 0xFFEA +Super_L = 0xFFEB +Super_R = 0xFFEC +Hyper_L = 0xFFED +Hyper_R = 0xFFEE +ISO_Lock = 0xFE01 +ISO_Level2_Latch = 0xFE02 +ISO_Level3_Shift = 0xFE03 +ISO_Level3_Latch = 0xFE04 +ISO_Level3_Lock = 0xFE05 +ISO_Group_Shift = 0xFF7E +ISO_Group_Latch = 0xFE06 +ISO_Group_Lock = 0xFE07 +ISO_Next_Group = 0xFE08 +ISO_Next_Group_Lock = 0xFE09 +ISO_Prev_Group = 0xFE0A +ISO_Prev_Group_Lock = 0xFE0B +ISO_First_Group = 0xFE0C +ISO_First_Group_Lock = 0xFE0D +ISO_Last_Group = 0xFE0E +ISO_Last_Group_Lock = 0xFE0F +ISO_Left_Tab = 0xFE20 +ISO_Move_Line_Up = 0xFE21 +ISO_Move_Line_Down = 0xFE22 +ISO_Partial_Line_Up = 0xFE23 +ISO_Partial_Line_Down = 0xFE24 +ISO_Partial_Space_Left = 0xFE25 +ISO_Partial_Space_Right = 0xFE26 +ISO_Set_Margin_Left = 0xFE27 +ISO_Set_Margin_Right = 0xFE28 +ISO_Release_Margin_Left = 0xFE29 +ISO_Release_Margin_Right = 0xFE2A +ISO_Release_Both_Margins = 0xFE2B +ISO_Fast_Cursor_Left = 0xFE2C +ISO_Fast_Cursor_Right = 0xFE2D +ISO_Fast_Cursor_Up = 0xFE2E +ISO_Fast_Cursor_Down = 0xFE2F +ISO_Continuous_Underline = 0xFE30 +ISO_Discontinuous_Underline = 0xFE31 +ISO_Emphasize = 0xFE32 +ISO_Center_Object = 0xFE33 +ISO_Enter = 0xFE34 +dead_grave = 0xFE50 +dead_acute = 0xFE51 +dead_circumflex = 0xFE52 +dead_tilde = 0xFE53 +dead_macron = 0xFE54 +dead_breve = 0xFE55 +dead_abovedot = 0xFE56 +dead_diaeresis = 0xFE57 +dead_abovering = 0xFE58 +dead_doubleacute = 0xFE59 +dead_caron = 0xFE5A +dead_cedilla = 0xFE5B +dead_ogonek = 0xFE5C +dead_iota = 0xFE5D +dead_voiced_sound = 0xFE5E +dead_semivoiced_sound = 0xFE5F +dead_belowdot = 0xFE60 +First_Virtual_Screen = 0xFED0 +Prev_Virtual_Screen = 0xFED1 +Next_Virtual_Screen = 0xFED2 +Last_Virtual_Screen = 0xFED4 +Terminate_Server = 0xFED5 +AccessX_Enable = 0xFE70 +AccessX_Feedback_Enable = 0xFE71 +RepeatKeys_Enable = 0xFE72 +SlowKeys_Enable = 0xFE73 +BounceKeys_Enable = 0xFE74 +StickyKeys_Enable = 0xFE75 +MouseKeys_Enable = 0xFE76 +MouseKeys_Accel_Enable = 0xFE77 +Overlay1_Enable = 0xFE78 +Overlay2_Enable = 0xFE79 +AudibleBell_Enable = 0xFE7A +Pointer_Left = 0xFEE0 +Pointer_Right = 0xFEE1 +Pointer_Up = 0xFEE2 +Pointer_Down = 0xFEE3 +Pointer_UpLeft = 0xFEE4 +Pointer_UpRight = 0xFEE5 +Pointer_DownLeft = 0xFEE6 +Pointer_DownRight = 0xFEE7 +Pointer_Button_Dflt = 0xFEE8 +Pointer_Button1 = 0xFEE9 +Pointer_Button2 = 0xFEEA +Pointer_Button3 = 0xFEEB +Pointer_Button4 = 0xFEEC +Pointer_Button5 = 0xFEED +Pointer_DblClick_Dflt = 0xFEEE +Pointer_DblClick1 = 0xFEEF +Pointer_DblClick2 = 0xFEF0 +Pointer_DblClick3 = 0xFEF1 +Pointer_DblClick4 = 0xFEF2 +Pointer_DblClick5 = 0xFEF3 +Pointer_Drag_Dflt = 0xFEF4 +Pointer_Drag1 = 0xFEF5 +Pointer_Drag2 = 0xFEF6 +Pointer_Drag3 = 0xFEF7 +Pointer_Drag4 = 0xFEF8 +Pointer_Drag5 = 0xFEFD +Pointer_EnableKeys = 0xFEF9 +Pointer_Accelerate = 0xFEFA +Pointer_DfltBtnNext = 0xFEFB +Pointer_DfltBtnPrev = 0xFEFC +_3270_Duplicate = 0xFD01 +_3270_FieldMark = 0xFD02 +_3270_Right2 = 0xFD03 +_3270_Left2 = 0xFD04 +_3270_BackTab = 0xFD05 +_3270_EraseEOF = 0xFD06 +_3270_EraseInput = 0xFD07 +_3270_Reset = 0xFD08 +_3270_Quit = 0xFD09 +_3270_PA1 = 0xFD0A +_3270_PA2 = 0xFD0B +_3270_PA3 = 0xFD0C +_3270_Test = 0xFD0D +_3270_Attn = 0xFD0E +_3270_CursorBlink = 0xFD0F +_3270_AltCursor = 0xFD10 +_3270_KeyClick = 0xFD11 +_3270_Jump = 0xFD12 +_3270_Ident = 0xFD13 +_3270_Rule = 0xFD14 +_3270_Copy = 0xFD15 +_3270_Play = 0xFD16 +_3270_Setup = 0xFD17 +_3270_Record = 0xFD18 +_3270_ChangeScreen = 0xFD19 +_3270_DeleteWord = 0xFD1A +_3270_ExSelect = 0xFD1B +_3270_CursorSelect = 0xFD1C +_3270_PrintScreen = 0xFD1D +_3270_Enter = 0xFD1E +space = 0x020 +exclam = 0x021 +quotedbl = 0x022 +numbersign = 0x023 +dollar = 0x024 +percent = 0x025 +ampersand = 0x026 +apostrophe = 0x027 +quoteright = 0x027 +parenleft = 0x028 +parenright = 0x029 +asterisk = 0x02a +plus = 0x02b +comma = 0x02c +minus = 0x02d +period = 0x02e +slash = 0x02f +_0 = 0x030 +_1 = 0x031 +_2 = 0x032 +_3 = 0x033 +_4 = 0x034 +_5 = 0x035 +_6 = 0x036 +_7 = 0x037 +_8 = 0x038 +_9 = 0x039 +colon = 0x03a +semicolon = 0x03b +less = 0x03c +equal = 0x03d +greater = 0x03e +question = 0x03f +at = 0x040 +A = 0x041 +B = 0x042 +C = 0x043 +D = 0x044 +E = 0x045 +F = 0x046 +G = 0x047 +H = 0x048 +I = 0x049 +J = 0x04a +K = 0x04b +L = 0x04c +M = 0x04d +N = 0x04e +O = 0x04f +P = 0x050 +Q = 0x051 +R = 0x052 +S = 0x053 +T = 0x054 +U = 0x055 +V = 0x056 +W = 0x057 +X = 0x058 +Y = 0x059 +Z = 0x05a +bracketleft = 0x05b +backslash = 0x05c +bracketright = 0x05d +asciicircum = 0x05e +underscore = 0x05f +grave = 0x060 +quoteleft = 0x060 +a = 0x061 +b = 0x062 +c = 0x063 +d = 0x064 +e = 0x065 +f = 0x066 +g = 0x067 +h = 0x068 +i = 0x069 +j = 0x06a +k = 0x06b +l = 0x06c +m = 0x06d +n = 0x06e +o = 0x06f +p = 0x070 +q = 0x071 +r = 0x072 +s = 0x073 +t = 0x074 +u = 0x075 +v = 0x076 +w = 0x077 +x = 0x078 +y = 0x079 +z = 0x07a +braceleft = 0x07b +bar = 0x07c +braceright = 0x07d +asciitilde = 0x07e +nobreakspace = 0x0a0 +exclamdown = 0x0a1 +cent = 0x0a2 +sterling = 0x0a3 +currency = 0x0a4 +yen = 0x0a5 +brokenbar = 0x0a6 +section = 0x0a7 +diaeresis = 0x0a8 +copyright = 0x0a9 +ordfeminine = 0x0aa +guillemotleft = 0x0ab +notsign = 0x0ac +hyphen = 0x0ad +registered = 0x0ae +macron = 0x0af +degree = 0x0b0 +plusminus = 0x0b1 +twosuperior = 0x0b2 +threesuperior = 0x0b3 +acute = 0x0b4 +mu = 0x0b5 +paragraph = 0x0b6 +periodcentered = 0x0b7 +cedilla = 0x0b8 +onesuperior = 0x0b9 +masculine = 0x0ba +guillemotright = 0x0bb +onequarter = 0x0bc +onehalf = 0x0bd +threequarters = 0x0be +questiondown = 0x0bf +Agrave = 0x0c0 +Aacute = 0x0c1 +Acircumflex = 0x0c2 +Atilde = 0x0c3 +Adiaeresis = 0x0c4 +Aring = 0x0c5 +AE = 0x0c6 +Ccedilla = 0x0c7 +Egrave = 0x0c8 +Eacute = 0x0c9 +Ecircumflex = 0x0ca +Ediaeresis = 0x0cb +Igrave = 0x0cc +Iacute = 0x0cd +Icircumflex = 0x0ce +Idiaeresis = 0x0cf +ETH = 0x0d0 +Eth = 0x0d0 +Ntilde = 0x0d1 +Ograve = 0x0d2 +Oacute = 0x0d3 +Ocircumflex = 0x0d4 +Otilde = 0x0d5 +Odiaeresis = 0x0d6 +multiply = 0x0d7 +Ooblique = 0x0d8 +Ugrave = 0x0d9 +Uacute = 0x0da +Ucircumflex = 0x0db +Udiaeresis = 0x0dc +Yacute = 0x0dd +THORN = 0x0de +Thorn = 0x0de +ssharp = 0x0df +agrave = 0x0e0 +aacute = 0x0e1 +acircumflex = 0x0e2 +atilde = 0x0e3 +adiaeresis = 0x0e4 +aring = 0x0e5 +ae = 0x0e6 +ccedilla = 0x0e7 +egrave = 0x0e8 +eacute = 0x0e9 +ecircumflex = 0x0ea +ediaeresis = 0x0eb +igrave = 0x0ec +iacute = 0x0ed +icircumflex = 0x0ee +idiaeresis = 0x0ef +eth = 0x0f0 +ntilde = 0x0f1 +ograve = 0x0f2 +oacute = 0x0f3 +ocircumflex = 0x0f4 +otilde = 0x0f5 +odiaeresis = 0x0f6 +division = 0x0f7 +oslash = 0x0f8 +ugrave = 0x0f9 +uacute = 0x0fa +ucircumflex = 0x0fb +udiaeresis = 0x0fc +yacute = 0x0fd +thorn = 0x0fe +ydiaeresis = 0x0ff +Aogonek = 0x1a1 +breve = 0x1a2 +Lstroke = 0x1a3 +Lcaron = 0x1a5 +Sacute = 0x1a6 +Scaron = 0x1a9 +Scedilla = 0x1aa +Tcaron = 0x1ab +Zacute = 0x1ac +Zcaron = 0x1ae +Zabovedot = 0x1af +aogonek = 0x1b1 +ogonek = 0x1b2 +lstroke = 0x1b3 +lcaron = 0x1b5 +sacute = 0x1b6 +caron = 0x1b7 +scaron = 0x1b9 +scedilla = 0x1ba +tcaron = 0x1bb +zacute = 0x1bc +doubleacute = 0x1bd +zcaron = 0x1be +zabovedot = 0x1bf +Racute = 0x1c0 +Abreve = 0x1c3 +Lacute = 0x1c5 +Cacute = 0x1c6 +Ccaron = 0x1c8 +Eogonek = 0x1ca +Ecaron = 0x1cc +Dcaron = 0x1cf +Dstroke = 0x1d0 +Nacute = 0x1d1 +Ncaron = 0x1d2 +Odoubleacute = 0x1d5 +Rcaron = 0x1d8 +Uring = 0x1d9 +Udoubleacute = 0x1db +Tcedilla = 0x1de +racute = 0x1e0 +abreve = 0x1e3 +lacute = 0x1e5 +cacute = 0x1e6 +ccaron = 0x1e8 +eogonek = 0x1ea +ecaron = 0x1ec +dcaron = 0x1ef +dstroke = 0x1f0 +nacute = 0x1f1 +ncaron = 0x1f2 +odoubleacute = 0x1f5 +udoubleacute = 0x1fb +rcaron = 0x1f8 +uring = 0x1f9 +tcedilla = 0x1fe +abovedot = 0x1ff +Hstroke = 0x2a1 +Hcircumflex = 0x2a6 +Iabovedot = 0x2a9 +Gbreve = 0x2ab +Jcircumflex = 0x2ac +hstroke = 0x2b1 +hcircumflex = 0x2b6 +idotless = 0x2b9 +gbreve = 0x2bb +jcircumflex = 0x2bc +Cabovedot = 0x2c5 +Ccircumflex = 0x2c6 +Gabovedot = 0x2d5 +Gcircumflex = 0x2d8 +Ubreve = 0x2dd +Scircumflex = 0x2de +cabovedot = 0x2e5 +ccircumflex = 0x2e6 +gabovedot = 0x2f5 +gcircumflex = 0x2f8 +ubreve = 0x2fd +scircumflex = 0x2fe +kra = 0x3a2 +kappa = 0x3a2 +Rcedilla = 0x3a3 +Itilde = 0x3a5 +Lcedilla = 0x3a6 +Emacron = 0x3aa +Gcedilla = 0x3ab +Tslash = 0x3ac +rcedilla = 0x3b3 +itilde = 0x3b5 +lcedilla = 0x3b6 +emacron = 0x3ba +gcedilla = 0x3bb +tslash = 0x3bc +ENG = 0x3bd +eng = 0x3bf +Amacron = 0x3c0 +Iogonek = 0x3c7 +Eabovedot = 0x3cc +Imacron = 0x3cf +Ncedilla = 0x3d1 +Omacron = 0x3d2 +Kcedilla = 0x3d3 +Uogonek = 0x3d9 +Utilde = 0x3dd +Umacron = 0x3de +amacron = 0x3e0 +iogonek = 0x3e7 +eabovedot = 0x3ec +imacron = 0x3ef +ncedilla = 0x3f1 +omacron = 0x3f2 +kcedilla = 0x3f3 +uogonek = 0x3f9 +utilde = 0x3fd +umacron = 0x3fe +OE = 0x13bc +oe = 0x13bd +Ydiaeresis = 0x13be +overline = 0x47e +kana_fullstop = 0x4a1 +kana_openingbracket = 0x4a2 +kana_closingbracket = 0x4a3 +kana_comma = 0x4a4 +kana_conjunctive = 0x4a5 +kana_middledot = 0x4a5 +kana_WO = 0x4a6 +kana_a = 0x4a7 +kana_i = 0x4a8 +kana_u = 0x4a9 +kana_e = 0x4aa +kana_o = 0x4ab +kana_ya = 0x4ac +kana_yu = 0x4ad +kana_yo = 0x4ae +kana_tsu = 0x4af +kana_tu = 0x4af +prolongedsound = 0x4b0 +kana_A = 0x4b1 +kana_I = 0x4b2 +kana_U = 0x4b3 +kana_E = 0x4b4 +kana_O = 0x4b5 +kana_KA = 0x4b6 +kana_KI = 0x4b7 +kana_KU = 0x4b8 +kana_KE = 0x4b9 +kana_KO = 0x4ba +kana_SA = 0x4bb +kana_SHI = 0x4bc +kana_SU = 0x4bd +kana_SE = 0x4be +kana_SO = 0x4bf +kana_TA = 0x4c0 +kana_CHI = 0x4c1 +kana_TI = 0x4c1 +kana_TSU = 0x4c2 +kana_TU = 0x4c2 +kana_TE = 0x4c3 +kana_TO = 0x4c4 +kana_NA = 0x4c5 +kana_NI = 0x4c6 +kana_NU = 0x4c7 +kana_NE = 0x4c8 +kana_NO = 0x4c9 +kana_HA = 0x4ca +kana_HI = 0x4cb +kana_FU = 0x4cc +kana_HU = 0x4cc +kana_HE = 0x4cd +kana_HO = 0x4ce +kana_MA = 0x4cf +kana_MI = 0x4d0 +kana_MU = 0x4d1 +kana_ME = 0x4d2 +kana_MO = 0x4d3 +kana_YA = 0x4d4 +kana_YU = 0x4d5 +kana_YO = 0x4d6 +kana_RA = 0x4d7 +kana_RI = 0x4d8 +kana_RU = 0x4d9 +kana_RE = 0x4da +kana_RO = 0x4db +kana_WA = 0x4dc +kana_N = 0x4dd +voicedsound = 0x4de +semivoicedsound = 0x4df +kana_switch = 0xFF7E +Arabic_comma = 0x5ac +Arabic_semicolon = 0x5bb +Arabic_question_mark = 0x5bf +Arabic_hamza = 0x5c1 +Arabic_maddaonalef = 0x5c2 +Arabic_hamzaonalef = 0x5c3 +Arabic_hamzaonwaw = 0x5c4 +Arabic_hamzaunderalef = 0x5c5 +Arabic_hamzaonyeh = 0x5c6 +Arabic_alef = 0x5c7 +Arabic_beh = 0x5c8 +Arabic_tehmarbuta = 0x5c9 +Arabic_teh = 0x5ca +Arabic_theh = 0x5cb +Arabic_jeem = 0x5cc +Arabic_hah = 0x5cd +Arabic_khah = 0x5ce +Arabic_dal = 0x5cf +Arabic_thal = 0x5d0 +Arabic_ra = 0x5d1 +Arabic_zain = 0x5d2 +Arabic_seen = 0x5d3 +Arabic_sheen = 0x5d4 +Arabic_sad = 0x5d5 +Arabic_dad = 0x5d6 +Arabic_tah = 0x5d7 +Arabic_zah = 0x5d8 +Arabic_ain = 0x5d9 +Arabic_ghain = 0x5da +Arabic_tatweel = 0x5e0 +Arabic_feh = 0x5e1 +Arabic_qaf = 0x5e2 +Arabic_kaf = 0x5e3 +Arabic_lam = 0x5e4 +Arabic_meem = 0x5e5 +Arabic_noon = 0x5e6 +Arabic_ha = 0x5e7 +Arabic_heh = 0x5e7 +Arabic_waw = 0x5e8 +Arabic_alefmaksura = 0x5e9 +Arabic_yeh = 0x5ea +Arabic_fathatan = 0x5eb +Arabic_dammatan = 0x5ec +Arabic_kasratan = 0x5ed +Arabic_fatha = 0x5ee +Arabic_damma = 0x5ef +Arabic_kasra = 0x5f0 +Arabic_shadda = 0x5f1 +Arabic_sukun = 0x5f2 +Arabic_switch = 0xFF7E +Serbian_dje = 0x6a1 +Macedonia_gje = 0x6a2 +Cyrillic_io = 0x6a3 +Ukrainian_ie = 0x6a4 +Ukranian_je = 0x6a4 +Macedonia_dse = 0x6a5 +Ukrainian_i = 0x6a6 +Ukranian_i = 0x6a6 +Ukrainian_yi = 0x6a7 +Ukranian_yi = 0x6a7 +Cyrillic_je = 0x6a8 +Serbian_je = 0x6a8 +Cyrillic_lje = 0x6a9 +Serbian_lje = 0x6a9 +Cyrillic_nje = 0x6aa +Serbian_nje = 0x6aa +Serbian_tshe = 0x6ab +Macedonia_kje = 0x6ac +Ukrainian_ghe_with_upturn = 0x6ad +Byelorussian_shortu = 0x6ae +Cyrillic_dzhe = 0x6af +Serbian_dze = 0x6af +numerosign = 0x6b0 +Serbian_DJE = 0x6b1 +Macedonia_GJE = 0x6b2 +Cyrillic_IO = 0x6b3 +Ukrainian_IE = 0x6b4 +Ukranian_JE = 0x6b4 +Macedonia_DSE = 0x6b5 +Ukrainian_I = 0x6b6 +Ukranian_I = 0x6b6 +Ukrainian_YI = 0x6b7 +Ukranian_YI = 0x6b7 +Cyrillic_JE = 0x6b8 +Serbian_JE = 0x6b8 +Cyrillic_LJE = 0x6b9 +Serbian_LJE = 0x6b9 +Cyrillic_NJE = 0x6ba +Serbian_NJE = 0x6ba +Serbian_TSHE = 0x6bb +Macedonia_KJE = 0x6bc +Ukrainian_GHE_WITH_UPTURN = 0x6bd +Byelorussian_SHORTU = 0x6be +Cyrillic_DZHE = 0x6bf +Serbian_DZE = 0x6bf +Cyrillic_yu = 0x6c0 +Cyrillic_a = 0x6c1 +Cyrillic_be = 0x6c2 +Cyrillic_tse = 0x6c3 +Cyrillic_de = 0x6c4 +Cyrillic_ie = 0x6c5 +Cyrillic_ef = 0x6c6 +Cyrillic_ghe = 0x6c7 +Cyrillic_ha = 0x6c8 +Cyrillic_i = 0x6c9 +Cyrillic_shorti = 0x6ca +Cyrillic_ka = 0x6cb +Cyrillic_el = 0x6cc +Cyrillic_em = 0x6cd +Cyrillic_en = 0x6ce +Cyrillic_o = 0x6cf +Cyrillic_pe = 0x6d0 +Cyrillic_ya = 0x6d1 +Cyrillic_er = 0x6d2 +Cyrillic_es = 0x6d3 +Cyrillic_te = 0x6d4 +Cyrillic_u = 0x6d5 +Cyrillic_zhe = 0x6d6 +Cyrillic_ve = 0x6d7 +Cyrillic_softsign = 0x6d8 +Cyrillic_yeru = 0x6d9 +Cyrillic_ze = 0x6da +Cyrillic_sha = 0x6db +Cyrillic_e = 0x6dc +Cyrillic_shcha = 0x6dd +Cyrillic_che = 0x6de +Cyrillic_hardsign = 0x6df +Cyrillic_YU = 0x6e0 +Cyrillic_A = 0x6e1 +Cyrillic_BE = 0x6e2 +Cyrillic_TSE = 0x6e3 +Cyrillic_DE = 0x6e4 +Cyrillic_IE = 0x6e5 +Cyrillic_EF = 0x6e6 +Cyrillic_GHE = 0x6e7 +Cyrillic_HA = 0x6e8 +Cyrillic_I = 0x6e9 +Cyrillic_SHORTI = 0x6ea +Cyrillic_KA = 0x6eb +Cyrillic_EL = 0x6ec +Cyrillic_EM = 0x6ed +Cyrillic_EN = 0x6ee +Cyrillic_O = 0x6ef +Cyrillic_PE = 0x6f0 +Cyrillic_YA = 0x6f1 +Cyrillic_ER = 0x6f2 +Cyrillic_ES = 0x6f3 +Cyrillic_TE = 0x6f4 +Cyrillic_U = 0x6f5 +Cyrillic_ZHE = 0x6f6 +Cyrillic_VE = 0x6f7 +Cyrillic_SOFTSIGN = 0x6f8 +Cyrillic_YERU = 0x6f9 +Cyrillic_ZE = 0x6fa +Cyrillic_SHA = 0x6fb +Cyrillic_E = 0x6fc +Cyrillic_SHCHA = 0x6fd +Cyrillic_CHE = 0x6fe +Cyrillic_HARDSIGN = 0x6ff +Greek_ALPHAaccent = 0x7a1 +Greek_EPSILONaccent = 0x7a2 +Greek_ETAaccent = 0x7a3 +Greek_IOTAaccent = 0x7a4 +Greek_IOTAdiaeresis = 0x7a5 +Greek_OMICRONaccent = 0x7a7 +Greek_UPSILONaccent = 0x7a8 +Greek_UPSILONdieresis = 0x7a9 +Greek_OMEGAaccent = 0x7ab +Greek_accentdieresis = 0x7ae +Greek_horizbar = 0x7af +Greek_alphaaccent = 0x7b1 +Greek_epsilonaccent = 0x7b2 +Greek_etaaccent = 0x7b3 +Greek_iotaaccent = 0x7b4 +Greek_iotadieresis = 0x7b5 +Greek_iotaaccentdieresis = 0x7b6 +Greek_omicronaccent = 0x7b7 +Greek_upsilonaccent = 0x7b8 +Greek_upsilondieresis = 0x7b9 +Greek_upsilonaccentdieresis = 0x7ba +Greek_omegaaccent = 0x7bb +Greek_ALPHA = 0x7c1 +Greek_BETA = 0x7c2 +Greek_GAMMA = 0x7c3 +Greek_DELTA = 0x7c4 +Greek_EPSILON = 0x7c5 +Greek_ZETA = 0x7c6 +Greek_ETA = 0x7c7 +Greek_THETA = 0x7c8 +Greek_IOTA = 0x7c9 +Greek_KAPPA = 0x7ca +Greek_LAMDA = 0x7cb +Greek_LAMBDA = 0x7cb +Greek_MU = 0x7cc +Greek_NU = 0x7cd +Greek_XI = 0x7ce +Greek_OMICRON = 0x7cf +Greek_PI = 0x7d0 +Greek_RHO = 0x7d1 +Greek_SIGMA = 0x7d2 +Greek_TAU = 0x7d4 +Greek_UPSILON = 0x7d5 +Greek_PHI = 0x7d6 +Greek_CHI = 0x7d7 +Greek_PSI = 0x7d8 +Greek_OMEGA = 0x7d9 +Greek_alpha = 0x7e1 +Greek_beta = 0x7e2 +Greek_gamma = 0x7e3 +Greek_delta = 0x7e4 +Greek_epsilon = 0x7e5 +Greek_zeta = 0x7e6 +Greek_eta = 0x7e7 +Greek_theta = 0x7e8 +Greek_iota = 0x7e9 +Greek_kappa = 0x7ea +Greek_lamda = 0x7eb +Greek_lambda = 0x7eb +Greek_mu = 0x7ec +Greek_nu = 0x7ed +Greek_xi = 0x7ee +Greek_omicron = 0x7ef +Greek_pi = 0x7f0 +Greek_rho = 0x7f1 +Greek_sigma = 0x7f2 +Greek_finalsmallsigma = 0x7f3 +Greek_tau = 0x7f4 +Greek_upsilon = 0x7f5 +Greek_phi = 0x7f6 +Greek_chi = 0x7f7 +Greek_psi = 0x7f8 +Greek_omega = 0x7f9 +Greek_switch = 0xFF7E +leftradical = 0x8a1 +topleftradical = 0x8a2 +horizconnector = 0x8a3 +topintegral = 0x8a4 +botintegral = 0x8a5 +vertconnector = 0x8a6 +topleftsqbracket = 0x8a7 +botleftsqbracket = 0x8a8 +toprightsqbracket = 0x8a9 +botrightsqbracket = 0x8aa +topleftparens = 0x8ab +botleftparens = 0x8ac +toprightparens = 0x8ad +botrightparens = 0x8ae +leftmiddlecurlybrace = 0x8af +rightmiddlecurlybrace = 0x8b0 +topleftsummation = 0x8b1 +botleftsummation = 0x8b2 +topvertsummationconnector = 0x8b3 +botvertsummationconnector = 0x8b4 +toprightsummation = 0x8b5 +botrightsummation = 0x8b6 +rightmiddlesummation = 0x8b7 +lessthanequal = 0x8bc +notequal = 0x8bd +greaterthanequal = 0x8be +integral = 0x8bf +therefore = 0x8c0 +variation = 0x8c1 +infinity = 0x8c2 +nabla = 0x8c5 +approximate = 0x8c8 +similarequal = 0x8c9 +ifonlyif = 0x8cd +implies = 0x8ce +identical = 0x8cf +radical = 0x8d6 +includedin = 0x8da +includes = 0x8db +intersection = 0x8dc +union = 0x8dd +logicaland = 0x8de +logicalor = 0x8df +partialderivative = 0x8ef +function = 0x8f6 +leftarrow = 0x8fb +uparrow = 0x8fc +rightarrow = 0x8fd +downarrow = 0x8fe +blank = 0x9df +soliddiamond = 0x9e0 +checkerboard = 0x9e1 +ht = 0x9e2 +ff = 0x9e3 +cr = 0x9e4 +lf = 0x9e5 +nl = 0x9e8 +vt = 0x9e9 +lowrightcorner = 0x9ea +uprightcorner = 0x9eb +upleftcorner = 0x9ec +lowleftcorner = 0x9ed +crossinglines = 0x9ee +horizlinescan1 = 0x9ef +horizlinescan3 = 0x9f0 +horizlinescan5 = 0x9f1 +horizlinescan7 = 0x9f2 +horizlinescan9 = 0x9f3 +leftt = 0x9f4 +rightt = 0x9f5 +bott = 0x9f6 +topt = 0x9f7 +vertbar = 0x9f8 +emspace = 0xaa1 +enspace = 0xaa2 +em3space = 0xaa3 +em4space = 0xaa4 +digitspace = 0xaa5 +punctspace = 0xaa6 +thinspace = 0xaa7 +hairspace = 0xaa8 +emdash = 0xaa9 +endash = 0xaaa +signifblank = 0xaac +ellipsis = 0xaae +doubbaselinedot = 0xaaf +onethird = 0xab0 +twothirds = 0xab1 +onefifth = 0xab2 +twofifths = 0xab3 +threefifths = 0xab4 +fourfifths = 0xab5 +onesixth = 0xab6 +fivesixths = 0xab7 +careof = 0xab8 +figdash = 0xabb +leftanglebracket = 0xabc +decimalpoint = 0xabd +rightanglebracket = 0xabe +marker = 0xabf +oneeighth = 0xac3 +threeeighths = 0xac4 +fiveeighths = 0xac5 +seveneighths = 0xac6 +trademark = 0xac9 +signaturemark = 0xaca +trademarkincircle = 0xacb +leftopentriangle = 0xacc +rightopentriangle = 0xacd +emopencircle = 0xace +emopenrectangle = 0xacf +leftsinglequotemark = 0xad0 +rightsinglequotemark = 0xad1 +leftdoublequotemark = 0xad2 +rightdoublequotemark = 0xad3 +prescription = 0xad4 +minutes = 0xad6 +seconds = 0xad7 +latincross = 0xad9 +hexagram = 0xada +filledrectbullet = 0xadb +filledlefttribullet = 0xadc +filledrighttribullet = 0xadd +emfilledcircle = 0xade +emfilledrect = 0xadf +enopencircbullet = 0xae0 +enopensquarebullet = 0xae1 +openrectbullet = 0xae2 +opentribulletup = 0xae3 +opentribulletdown = 0xae4 +openstar = 0xae5 +enfilledcircbullet = 0xae6 +enfilledsqbullet = 0xae7 +filledtribulletup = 0xae8 +filledtribulletdown = 0xae9 +leftpointer = 0xaea +rightpointer = 0xaeb +club = 0xaec +diamond = 0xaed +heart = 0xaee +maltesecross = 0xaf0 +dagger = 0xaf1 +doubledagger = 0xaf2 +checkmark = 0xaf3 +ballotcross = 0xaf4 +musicalsharp = 0xaf5 +musicalflat = 0xaf6 +malesymbol = 0xaf7 +femalesymbol = 0xaf8 +telephone = 0xaf9 +telephonerecorder = 0xafa +phonographcopyright = 0xafb +caret = 0xafc +singlelowquotemark = 0xafd +doublelowquotemark = 0xafe +cursor = 0xaff +leftcaret = 0xba3 +rightcaret = 0xba6 +downcaret = 0xba8 +upcaret = 0xba9 +overbar = 0xbc0 +downtack = 0xbc2 +upshoe = 0xbc3 +downstile = 0xbc4 +underbar = 0xbc6 +jot = 0xbca +quad = 0xbcc +uptack = 0xbce +circle = 0xbcf +upstile = 0xbd3 +downshoe = 0xbd6 +rightshoe = 0xbd8 +leftshoe = 0xbda +lefttack = 0xbdc +righttack = 0xbfc +hebrew_doublelowline = 0xcdf +hebrew_aleph = 0xce0 +hebrew_bet = 0xce1 +hebrew_beth = 0xce1 +hebrew_gimel = 0xce2 +hebrew_gimmel = 0xce2 +hebrew_dalet = 0xce3 +hebrew_daleth = 0xce3 +hebrew_he = 0xce4 +hebrew_waw = 0xce5 +hebrew_zain = 0xce6 +hebrew_zayin = 0xce6 +hebrew_chet = 0xce7 +hebrew_het = 0xce7 +hebrew_tet = 0xce8 +hebrew_teth = 0xce8 +hebrew_yod = 0xce9 +hebrew_finalkaph = 0xcea +hebrew_kaph = 0xceb +hebrew_lamed = 0xcec +hebrew_finalmem = 0xced +hebrew_mem = 0xcee +hebrew_finalnun = 0xcef +hebrew_nun = 0xcf0 +hebrew_samech = 0xcf1 +hebrew_samekh = 0xcf1 +hebrew_ayin = 0xcf2 +hebrew_finalpe = 0xcf3 +hebrew_pe = 0xcf4 +hebrew_finalzade = 0xcf5 +hebrew_finalzadi = 0xcf5 +hebrew_zade = 0xcf6 +hebrew_zadi = 0xcf6 +hebrew_qoph = 0xcf7 +hebrew_kuf = 0xcf7 +hebrew_resh = 0xcf8 +hebrew_shin = 0xcf9 +hebrew_taw = 0xcfa +hebrew_taf = 0xcfa +Hebrew_switch = 0xFF7E +Thai_kokai = 0xda1 +Thai_khokhai = 0xda2 +Thai_khokhuat = 0xda3 +Thai_khokhwai = 0xda4 +Thai_khokhon = 0xda5 +Thai_khorakhang = 0xda6 +Thai_ngongu = 0xda7 +Thai_chochan = 0xda8 +Thai_choching = 0xda9 +Thai_chochang = 0xdaa +Thai_soso = 0xdab +Thai_chochoe = 0xdac +Thai_yoying = 0xdad +Thai_dochada = 0xdae +Thai_topatak = 0xdaf +Thai_thothan = 0xdb0 +Thai_thonangmontho = 0xdb1 +Thai_thophuthao = 0xdb2 +Thai_nonen = 0xdb3 +Thai_dodek = 0xdb4 +Thai_totao = 0xdb5 +Thai_thothung = 0xdb6 +Thai_thothahan = 0xdb7 +Thai_thothong = 0xdb8 +Thai_nonu = 0xdb9 +Thai_bobaimai = 0xdba +Thai_popla = 0xdbb +Thai_phophung = 0xdbc +Thai_fofa = 0xdbd +Thai_phophan = 0xdbe +Thai_fofan = 0xdbf +Thai_phosamphao = 0xdc0 +Thai_moma = 0xdc1 +Thai_yoyak = 0xdc2 +Thai_rorua = 0xdc3 +Thai_ru = 0xdc4 +Thai_loling = 0xdc5 +Thai_lu = 0xdc6 +Thai_wowaen = 0xdc7 +Thai_sosala = 0xdc8 +Thai_sorusi = 0xdc9 +Thai_sosua = 0xdca +Thai_hohip = 0xdcb +Thai_lochula = 0xdcc +Thai_oang = 0xdcd +Thai_honokhuk = 0xdce +Thai_paiyannoi = 0xdcf +Thai_saraa = 0xdd0 +Thai_maihanakat = 0xdd1 +Thai_saraaa = 0xdd2 +Thai_saraam = 0xdd3 +Thai_sarai = 0xdd4 +Thai_saraii = 0xdd5 +Thai_saraue = 0xdd6 +Thai_sarauee = 0xdd7 +Thai_sarau = 0xdd8 +Thai_sarauu = 0xdd9 +Thai_phinthu = 0xdda +Thai_maihanakat_maitho = 0xdde +Thai_baht = 0xddf +Thai_sarae = 0xde0 +Thai_saraae = 0xde1 +Thai_sarao = 0xde2 +Thai_saraaimaimuan = 0xde3 +Thai_saraaimaimalai = 0xde4 +Thai_lakkhangyao = 0xde5 +Thai_maiyamok = 0xde6 +Thai_maitaikhu = 0xde7 +Thai_maiek = 0xde8 +Thai_maitho = 0xde9 +Thai_maitri = 0xdea +Thai_maichattawa = 0xdeb +Thai_thanthakhat = 0xdec +Thai_nikhahit = 0xded +Thai_leksun = 0xdf0 +Thai_leknung = 0xdf1 +Thai_leksong = 0xdf2 +Thai_leksam = 0xdf3 +Thai_leksi = 0xdf4 +Thai_lekha = 0xdf5 +Thai_lekhok = 0xdf6 +Thai_lekchet = 0xdf7 +Thai_lekpaet = 0xdf8 +Thai_lekkao = 0xdf9 +Hangul = 0xff31 +Hangul_Start = 0xff32 +Hangul_End = 0xff33 +Hangul_Hanja = 0xff34 +Hangul_Jamo = 0xff35 +Hangul_Romaja = 0xff36 +Hangul_Codeinput = 0xff37 +Hangul_Jeonja = 0xff38 +Hangul_Banja = 0xff39 +Hangul_PreHanja = 0xff3a +Hangul_PostHanja = 0xff3b +Hangul_SingleCandidate = 0xff3c +Hangul_MultipleCandidate = 0xff3d +Hangul_PreviousCandidate = 0xff3e +Hangul_Special = 0xff3f +Hangul_switch = 0xFF7E +Hangul_Kiyeog = 0xea1 +Hangul_SsangKiyeog = 0xea2 +Hangul_KiyeogSios = 0xea3 +Hangul_Nieun = 0xea4 +Hangul_NieunJieuj = 0xea5 +Hangul_NieunHieuh = 0xea6 +Hangul_Dikeud = 0xea7 +Hangul_SsangDikeud = 0xea8 +Hangul_Rieul = 0xea9 +Hangul_RieulKiyeog = 0xeaa +Hangul_RieulMieum = 0xeab +Hangul_RieulPieub = 0xeac +Hangul_RieulSios = 0xead +Hangul_RieulTieut = 0xeae +Hangul_RieulPhieuf = 0xeaf +Hangul_RieulHieuh = 0xeb0 +Hangul_Mieum = 0xeb1 +Hangul_Pieub = 0xeb2 +Hangul_SsangPieub = 0xeb3 +Hangul_PieubSios = 0xeb4 +Hangul_Sios = 0xeb5 +Hangul_SsangSios = 0xeb6 +Hangul_Ieung = 0xeb7 +Hangul_Jieuj = 0xeb8 +Hangul_SsangJieuj = 0xeb9 +Hangul_Cieuc = 0xeba +Hangul_Khieuq = 0xebb +Hangul_Tieut = 0xebc +Hangul_Phieuf = 0xebd +Hangul_Hieuh = 0xebe +Hangul_A = 0xebf +Hangul_AE = 0xec0 +Hangul_YA = 0xec1 +Hangul_YAE = 0xec2 +Hangul_EO = 0xec3 +Hangul_E = 0xec4 +Hangul_YEO = 0xec5 +Hangul_YE = 0xec6 +Hangul_O = 0xec7 +Hangul_WA = 0xec8 +Hangul_WAE = 0xec9 +Hangul_OE = 0xeca +Hangul_YO = 0xecb +Hangul_U = 0xecc +Hangul_WEO = 0xecd +Hangul_WE = 0xece +Hangul_WI = 0xecf +Hangul_YU = 0xed0 +Hangul_EU = 0xed1 +Hangul_YI = 0xed2 +Hangul_I = 0xed3 +Hangul_J_Kiyeog = 0xed4 +Hangul_J_SsangKiyeog = 0xed5 +Hangul_J_KiyeogSios = 0xed6 +Hangul_J_Nieun = 0xed7 +Hangul_J_NieunJieuj = 0xed8 +Hangul_J_NieunHieuh = 0xed9 +Hangul_J_Dikeud = 0xeda +Hangul_J_Rieul = 0xedb +Hangul_J_RieulKiyeog = 0xedc +Hangul_J_RieulMieum = 0xedd +Hangul_J_RieulPieub = 0xede +Hangul_J_RieulSios = 0xedf +Hangul_J_RieulTieut = 0xee0 +Hangul_J_RieulPhieuf = 0xee1 +Hangul_J_RieulHieuh = 0xee2 +Hangul_J_Mieum = 0xee3 +Hangul_J_Pieub = 0xee4 +Hangul_J_PieubSios = 0xee5 +Hangul_J_Sios = 0xee6 +Hangul_J_SsangSios = 0xee7 +Hangul_J_Ieung = 0xee8 +Hangul_J_Jieuj = 0xee9 +Hangul_J_Cieuc = 0xeea +Hangul_J_Khieuq = 0xeeb +Hangul_J_Tieut = 0xeec +Hangul_J_Phieuf = 0xeed +Hangul_J_Hieuh = 0xeee +Hangul_RieulYeorinHieuh = 0xeef +Hangul_SunkyeongeumMieum = 0xef0 +Hangul_SunkyeongeumPieub = 0xef1 +Hangul_PanSios = 0xef2 +Hangul_KkogjiDalrinIeung = 0xef3 +Hangul_SunkyeongeumPhieuf = 0xef4 +Hangul_YeorinHieuh = 0xef5 +Hangul_AraeA = 0xef6 +Hangul_AraeAE = 0xef7 +Hangul_J_PanSios = 0xef8 +Hangul_J_KkogjiDalrinIeung = 0xef9 +Hangul_J_YeorinHieuh = 0xefa +Korean_Won = 0xeff +Armenian_eternity = 0x14a1 +Armenian_section_sign = 0x14a2 +Armenian_full_stop = 0x14a3 +Armenian_verjaket = 0x14a3 +Armenian_parenright = 0x14a4 +Armenian_parenleft = 0x14a5 +Armenian_guillemotright = 0x14a6 +Armenian_guillemotleft = 0x14a7 +Armenian_em_dash = 0x14a8 +Armenian_dot = 0x14a9 +Armenian_mijaket = 0x14a9 +Armenian_separation_mark = 0x14aa +Armenian_but = 0x14aa +Armenian_comma = 0x14ab +Armenian_en_dash = 0x14ac +Armenian_hyphen = 0x14ad +Armenian_yentamna = 0x14ad +Armenian_ellipsis = 0x14ae +Armenian_exclam = 0x14af +Armenian_amanak = 0x14af +Armenian_accent = 0x14b0 +Armenian_shesht = 0x14b0 +Armenian_question = 0x14b1 +Armenian_paruyk = 0x14b1 +Armenian_AYB = 0x14b2 +Armenian_ayb = 0x14b3 +Armenian_BEN = 0x14b4 +Armenian_ben = 0x14b5 +Armenian_GIM = 0x14b6 +Armenian_gim = 0x14b7 +Armenian_DA = 0x14b8 +Armenian_da = 0x14b9 +Armenian_YECH = 0x14ba +Armenian_yech = 0x14bb +Armenian_ZA = 0x14bc +Armenian_za = 0x14bd +Armenian_E = 0x14be +Armenian_e = 0x14bf +Armenian_AT = 0x14c0 +Armenian_at = 0x14c1 +Armenian_TO = 0x14c2 +Armenian_to = 0x14c3 +Armenian_ZHE = 0x14c4 +Armenian_zhe = 0x14c5 +Armenian_INI = 0x14c6 +Armenian_ini = 0x14c7 +Armenian_LYUN = 0x14c8 +Armenian_lyun = 0x14c9 +Armenian_KHE = 0x14ca +Armenian_khe = 0x14cb +Armenian_TSA = 0x14cc +Armenian_tsa = 0x14cd +Armenian_KEN = 0x14ce +Armenian_ken = 0x14cf +Armenian_HO = 0x14d0 +Armenian_ho = 0x14d1 +Armenian_DZA = 0x14d2 +Armenian_dza = 0x14d3 +Armenian_GHAT = 0x14d4 +Armenian_ghat = 0x14d5 +Armenian_TCHE = 0x14d6 +Armenian_tche = 0x14d7 +Armenian_MEN = 0x14d8 +Armenian_men = 0x14d9 +Armenian_HI = 0x14da +Armenian_hi = 0x14db +Armenian_NU = 0x14dc +Armenian_nu = 0x14dd +Armenian_SHA = 0x14de +Armenian_sha = 0x14df +Armenian_VO = 0x14e0 +Armenian_vo = 0x14e1 +Armenian_CHA = 0x14e2 +Armenian_cha = 0x14e3 +Armenian_PE = 0x14e4 +Armenian_pe = 0x14e5 +Armenian_JE = 0x14e6 +Armenian_je = 0x14e7 +Armenian_RA = 0x14e8 +Armenian_ra = 0x14e9 +Armenian_SE = 0x14ea +Armenian_se = 0x14eb +Armenian_VEV = 0x14ec +Armenian_vev = 0x14ed +Armenian_TYUN = 0x14ee +Armenian_tyun = 0x14ef +Armenian_RE = 0x14f0 +Armenian_re = 0x14f1 +Armenian_TSO = 0x14f2 +Armenian_tso = 0x14f3 +Armenian_VYUN = 0x14f4 +Armenian_vyun = 0x14f5 +Armenian_PYUR = 0x14f6 +Armenian_pyur = 0x14f7 +Armenian_KE = 0x14f8 +Armenian_ke = 0x14f9 +Armenian_O = 0x14fa +Armenian_o = 0x14fb +Armenian_FE = 0x14fc +Armenian_fe = 0x14fd +Armenian_apostrophe = 0x14fe +Armenian_ligature_ew = 0x14ff +Georgian_an = 0x15d0 +Georgian_ban = 0x15d1 +Georgian_gan = 0x15d2 +Georgian_don = 0x15d3 +Georgian_en = 0x15d4 +Georgian_vin = 0x15d5 +Georgian_zen = 0x15d6 +Georgian_tan = 0x15d7 +Georgian_in = 0x15d8 +Georgian_kan = 0x15d9 +Georgian_las = 0x15da +Georgian_man = 0x15db +Georgian_nar = 0x15dc +Georgian_on = 0x15dd +Georgian_par = 0x15de +Georgian_zhar = 0x15df +Georgian_rae = 0x15e0 +Georgian_san = 0x15e1 +Georgian_tar = 0x15e2 +Georgian_un = 0x15e3 +Georgian_phar = 0x15e4 +Georgian_khar = 0x15e5 +Georgian_ghan = 0x15e6 +Georgian_qar = 0x15e7 +Georgian_shin = 0x15e8 +Georgian_chin = 0x15e9 +Georgian_can = 0x15ea +Georgian_jil = 0x15eb +Georgian_cil = 0x15ec +Georgian_char = 0x15ed +Georgian_xan = 0x15ee +Georgian_jhan = 0x15ef +Georgian_hae = 0x15f0 +Georgian_he = 0x15f1 +Georgian_hie = 0x15f2 +Georgian_we = 0x15f3 +Georgian_har = 0x15f4 +Georgian_hoe = 0x15f5 +Georgian_fi = 0x15f6 +EcuSign = 0x20a0 +ColonSign = 0x20a1 +CruzeiroSign = 0x20a2 +FFrancSign = 0x20a3 +LiraSign = 0x20a4 +MillSign = 0x20a5 +NairaSign = 0x20a6 +PesetaSign = 0x20a7 +RupeeSign = 0x20a8 +WonSign = 0x20a9 +NewSheqelSign = 0x20aa +DongSign = 0x20ab +EuroSign = 0x20ac diff --git a/ibus/object.py b/ibus/object.py new file mode 100644 index 0000000..af6a0fe --- /dev/null +++ b/ibus/object.py @@ -0,0 +1,14 @@ +import gobject + +class Object (gobject.GObject): + __gsignals__ = { + 'destroy' : ( + gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ()) + } + + def destroy (self): + self.emit ("destroy") + +gobject.type_register (Object) diff --git a/ibus/server.py b/ibus/server.py new file mode 100755 index 0000000..6bd6642 --- /dev/null +++ b/ibus/server.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +import dbus +import dbus.server +import dbus.lowlevel +import dbus.mainloop.glib +import gobject +from ibus import keysyms +from ibus import attribute +from ibus import client +from ibus import interface +from ibus import IBus +from ibus import Object + +from ibus.common import \ + IBUS_PATH, \ + IBUS_ADDR + +class IBusServer (Object): + def __init__ (self): + Object.__init__ (self) + self._server = dbus.server.Server (IBUS_ADDR) + self._ibus = IBus () + self._server.register_object (self._ibus, IBUS_PATH) + + def get_address (self): + return self._server.get_address () + + def new_connection (self, server, connection): + connection.add_message_filter (self.message_filter_cb) + pass + + def remove_connection (self, connection): + pass + + def message_filter_cb (self, connection, message): + if message.get_interface() == "org.freedesktop.DBus.Local" and \ + message.get_member() == "Disconnected": + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + + print "Got a Message (%s) : " % message.__class__.__name__ + print "\t From: %s" % message.get_sender () + print "\t To: %s" % message.get_destination () + print "\t Interface: %s" % message.get_interface () + print "\t Path: %s" % message.get_path () + print "\t Member: %s" % message.get_member () + print "\t Arguments:" + i = 0 + for arg in message.get_args_list(): + print "\t\t Arg[%d] : %s" % (i, arg) + i = i + 1 + + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + +if __name__ == "__main__": + dbus.mainloop.glib.DBusGMainLoop (set_as_default = True) + loop = gobject.MainLoop () + bus = IBusServer () + print "IBUS_ADDRESS=\"%s\"" % bus.get_address () + loop.run () + diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 0000000..c5ec3b2 --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1,27 @@ +# vim:set noet ts=4: +# +# gik - The G Input Toolkit +# +# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com> +# +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA +# +# $Id: $ +# +EXTRA_DIST = \ + as-version.m4 \ + $(NULL) diff --git a/m4/as-version.m4 b/m4/as-version.m4 new file mode 100644 index 0000000..a5b4399 --- /dev/null +++ b/m4/as-version.m4 @@ -0,0 +1,71 @@ +dnl as-version.m4 0.2.0 + +dnl autostars m4 macro for versioning + +dnl Thomas Vander Stichele <thomas at apestaart dot org> + +dnl $Id: as-version.m4,v 1.4 2004/06/01 09:40:05 thomasvs Exp $ + +dnl AS_VERSION + +dnl example +dnl AS_VERSION + +dnl this macro +dnl - AC_SUBST's PACKAGE_VERSION_MAJOR, _MINOR, _MICRO +dnl - AC_SUBST's PACKAGE_VERSION_RELEASE, +dnl which can be used for rpm release fields +dnl - doesn't call AM_INIT_AUTOMAKE anymore because it prevents +dnl maintainer mode from running correctly +dnl +dnl don't forget to put #undef PACKAGE_VERSION_RELEASE in acconfig.h +dnl if you use acconfig.h + +AC_DEFUN([AS_VERSION], +[ + PACKAGE_VERSION_MAJOR=$(echo AC_PACKAGE_VERSION | cut -d'.' -f1) + PACKAGE_VERSION_MINOR=$(echo AC_PACKAGE_VERSION | cut -d'.' -f2) + PACKAGE_VERSION_MICRO=$(echo AC_PACKAGE_VERSION | cut -d'.' -f3) + + AC_SUBST(PACKAGE_VERSION_MAJOR) + AC_SUBST(PACKAGE_VERSION_MINOR) + AC_SUBST(PACKAGE_VERSION_MICRO) +]) + +dnl AS_NANO(ACTION-IF-NO-NANO, [ACTION-IF-NANO]) + +dnl requires AC_INIT to be called before +dnl For projects using a fourth or nano number in your versioning to indicate +dnl development or prerelease snapshots, this macro allows the build to be +dnl set up differently accordingly. + +dnl this macro: +dnl - parses AC_PACKAGE_VERSION, set by AC_INIT, and extracts the nano number +dnl - sets the variable PACKAGE_VERSION_NANO +dnl - sets the variable PACKAGE_VERSION_RELEASE, which can be used +dnl for rpm release fields +dnl - executes ACTION-IF-NO-NANO or ACTION-IF-NANO + +dnl example: +dnl AS_NANO(RELEASE="yes", RELEASE="no") + +AC_DEFUN([AS_NANO], +[ + AC_MSG_CHECKING(nano version) + + NANO=$(echo AC_PACKAGE_VERSION | cut -d'.' -f4) + + if test x"$NANO" = x || test "x$NANO" = "x0" ; then + AC_MSG_RESULT([0 (release)]) + NANO=0 + PACKAGE_VERSION_RELEASE=1 + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT($NANO) + PACKAGE_VERSION_RELEASE=0.`date +%Y%m%d.%H%M%S` + ifelse([$2], , :, [$2]) + fi + PACKAGE_VERSION_NANO=$NANO + AC_SUBST(PACKAGE_VERSION_NANO) + AC_SUBST(PACKAGE_VERSION_RELEASE) +]) diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 0000000..7d73dbd --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Huang Peng <shawn.p.huang@gmail.com> + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = $(PACKAGE_BUGREPORT) + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/po/POTFILES.in |