Blob Blame History Raw
From 571e4ab3e28acb90466ac58e3fe9f4efc4b8ba0e Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Sun, 4 Mar 2012 22:16:01 +0900
Subject: [PATCH] Add ibus-xkb and libgnomekbd.

---
 configure.ac                  |   59 +++++
 data/ibus.schemas.in          |   58 +++++
 engine/Makefile.am            |   22 ++
 engine/ibus-xkb-main.c        |  111 +++++++++
 engine/main.vala              |   86 +++++++
 engine/simple.xml.in.in       |  531 +----------------------------------------
 engine/xkblib.c               |  327 +++++++++++++++++++++++++
 engine/xkblib.h               |   41 ++++
 ibus-1.0.pc.in                |    4 +
 setup/enginecombobox.py       |    6 +-
 setup/main.py                 |   17 ++-
 src/Makefile.am               |    5 +
 src/ibus.h                    |    1 +
 src/ibusxkbxml.c              |  466 ++++++++++++++++++++++++++++++++++++
 src/ibusxkbxml.h              |  187 +++++++++++++++
 ui/gtk3/Gkbd-3.0.metadata     |    1 +
 ui/gtk3/Makefile.am           |   45 ++++
 ui/gtk3/gkbdlayout.vala.false |   63 +++++
 ui/gtk3/gkbdlayout.vala.true  |  111 +++++++++
 ui/gtk3/panel.vala            |  275 ++++++++++++++++++++--
 ui/gtk3/xkblayout.vala        |  466 ++++++++++++++++++++++++++++++++++++
 21 files changed, 2331 insertions(+), 551 deletions(-)
 create mode 100644 engine/ibus-xkb-main.c
 create mode 100644 engine/xkblib.c
 create mode 100644 engine/xkblib.h
 create mode 100644 src/ibusxkbxml.c
 create mode 100644 src/ibusxkbxml.h
 create mode 100644 ui/gtk3/Gkbd-3.0.metadata
 create mode 100644 ui/gtk3/gkbdlayout.vala.false
 create mode 100644 ui/gtk3/gkbdlayout.vala.true
 create mode 100644 ui/gtk3/xkblayout.vala

diff --git a/configure.ac b/configure.ac
index aeb22b4..a516c94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,6 +249,63 @@ else
     enable_xim="no (disabled, use --enable-xim to enable)"
 fi
 
+# --enable-xkb option.
+AC_ARG_ENABLE(xkb,
+    AS_HELP_STRING([--enable-xkb],
+                   [Build xkb]),
+    [enable_xkb=$enableval],
+    [enable_xkb=no]
+)
+AM_CONDITIONAL([ENABLE_XKB], [test x"$enable_xkb" = x"yes"])
+if test x"$enable_xkb" = x"yes"; then
+    PKG_CHECK_MODULES(X11, [
+        x11
+    ])
+    PKG_CHECK_MODULES(XKB,
+        [xkbfile],,
+        [XKB_LIBS="-lxkbfile"]
+    )
+    AC_DEFINE(HAVE_XKB, 1, [define to 1 if you have xkbfile])
+    HAVE_IBUS_XKB=true
+else
+    enable_xkb="no (disabled, use --enable-xkb to enable)"
+    HAVE_IBUS_XKB=false
+fi
+AC_SUBST(HAVE_IBUS_XKB)
+
+# --enable-libgnomekbd option.
+AC_ARG_ENABLE(libgnomekbd,
+    AS_HELP_STRING([--enable-libgnomekbd],
+                   [Use libgnomekbd to handle the keymaps]),
+    [enable_libgnomekbd=$enableval],
+    [enable_libgnomekbd=no]
+)
+AM_CONDITIONAL([ENABLE_LIBGNOMEKBD], [test x"$enable_libgnomekbd" = x"yes"])
+if test x"$enable_libgnomekbd" = x"yes"; then
+    # check for libgnomekbd
+    PKG_CHECK_MODULES(LIBGNOMEKBDUI, [
+        libgnomekbdui
+    ])
+    PKG_CHECK_MODULES(ATK, [
+        atk
+    ])
+    HAVE_IBUS_GKBD=true
+else
+    enable_libgnomekbd="no (disabled, use --enable-libgnomekbd to enable)"
+    HAVE_IBUS_GKBD=false
+fi
+AC_SUBST(HAVE_IBUS_GKBD)
+
+# Define XKB rules file
+AC_ARG_WITH(xkb-rules-xml,
+    AS_HELP_STRING([--with-xkb-rules-xml[=$DIR/evdev.xml]],
+        [Set evdev.xml file path (default: /usr/share/X11/xkb/rules/evdev.xml)]),
+    XKB_RULES_XML_FILE=$with_xkb_rules_xml,
+    XKB_RULES_XML_FILE="/usr/share/X11/xkb/rules/evdev.xml"
+)
+AC_DEFINE_UNQUOTED(XKB_RULES_XML_FILE, "$XKB_RULES_XML_FILE",
+    [Define file path of evdev.xml])
+
 # GObject introspection
 GOBJECT_INTROSPECTION_CHECK([0.6.8])
 
@@ -545,5 +602,7 @@ Build options:
   Panel icon                "$IBUS_ICON_KEYBOARD"
   Enable surrounding-text   $enable_surrounding_text
   Run test cases            $enable_tests
+  Build XKB                 $enable_xkb
+  Build libgnomebkd         $enable_libgnomekbd
 ])
 
diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
index 53ba05c..3ca0fbc 100644
--- a/data/ibus.schemas.in
+++ b/data/ibus.schemas.in
@@ -38,6 +38,18 @@
       </locale>
     </schema>
     <schema>
+      <key>/schemas/desktop/ibus/general/hotkey/trigger-accel</key>
+      <applyto>/desktop/ibus/general/hotkey/trigger-accel</applyto>
+      <owner>ibus</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default>[&lt;Control&gt;space]</default>
+      <locale name="C">
+        <short>Trigger shortcut keys for gtk_accelerator_parse</short>
+	    <long>The shortcut keys for turning input method on or off</long>
+      </locale>
+    </schema>
+    <schema>
       <key>/schemas/desktop/ibus/general/hotkey/enable_unconditional</key>
       <applyto>/desktop/ibus/general/hotkey/enable_unconditional</applyto>
       <owner>ibus</owner>
@@ -203,6 +215,52 @@
       </locale>
     </schema>
     <schema>
+      <key>/schemas/desktop/ibus/general/use_xmodmap</key>
+      <applyto>/desktop/ibus/general/use_xmodmap</applyto>
+      <owner>ibus</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Use xmodmap</short>
+           <long>Run xmodmap if .xmodmap/.Xmodmap exists.</long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/ibus/general/xkb_latin_layouts</key>
+      <applyto>/desktop/ibus/general/xkb_latin_layouts</applyto>
+      <owner>ibus</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default>[ara,bg,cz,dev,gr,gur,in,jp(kana),mal,mkd,ru,ua]</default>
+      <locale name="C">
+        <short>Latin layout which have no ASCII</short>
+           <long>us layout is appended to the latin layouts. variant is not needed.</long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/ibus/general/load_xkb_layouts</key>
+      <applyto>/desktop/ibus/general/load_xkb_layouts</applyto>
+      <owner>ibus</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default>[us,us(chr),us(dvorak),ad,al,am,ara,az,ba,bd,be,bg,br,bt,by,
+de,dk,ca,ch,cn(tib),cz,ee,epo,es,et,fi,fo,fr,
+gb,ge,ge(dsb),ge(ru),ge(os),gh,gh(akan),gh(ewe),gh(fula),gh(ga),gh(hausa),
+gn,gr,hu,hr,ie,ie(CloGaelach),il,
+in,
+in(tel),in(bolnagri),iq,iq(ku),ir,ir(ku),is,it,jp,
+kg,kh,kz,la,latam,lk,lk(tam_unicode),lt,lv,ma,ma(tifinagh),mal,mao,
+me,mk,mm,mt,mv,ng,ng(hausa),ng,ng(igbo),ng(yoruba),nl,no,no(smi),np,
+pk,pl,pl(csb),pt,ro,rs,ru,ru(cv),ru(kom),ru(sah),ru(tt),ru(xal),
+se,si,sk,sy,sy(ku),th,tj,tr,ua,uz,vn
+]</default>
+      <locale name="C">
+        <short>XKB layout list which is shown on ibus-setup</short>
+           <long>XKB layout list which is shown on ibus-setup.
+                 The format is "layout" or "layout(variant)".</long>
+      </locale>
+    </schema>
+    <schema>
       <key>/schemas/desktop/ibus/panel/use_custom_font</key>
       <applyto>/desktop/ibus/panel/use_custom_font</applyto>
       <owner>ibus</owner>
diff --git a/engine/Makefile.am b/engine/Makefile.am
index b3b46be..af4747d 100644
--- a/engine/Makefile.am
+++ b/engine/Makefile.am
@@ -25,6 +25,8 @@ libibus = $(top_builddir)/src/libibus-@IBUS_API_VERSION@.la
 INCLUDES = \
 	-I$(top_srcdir)/src \
 	-I$(top_builddir)/src \
+	-DIBUS_LOCALEDIR=\"$(datadir)/locale\" \
+	-DLIBEXECDIR=\""$(libexecdir)"\" \
 	$(NULL)
 
 AM_CFLAGS = \
@@ -78,6 +80,26 @@ component_DATA = \
 
 componentdir = $(pkgdatadir)/component
 
+if ENABLE_XKB
+libexec_PROGRAMS += ibus-xkb
+ibus_xkb_SOURCES = \
+    ibus-xkb-main.c \
+    xkblib.h \
+    xkblib.c \
+    $(NULL)
+ibus_xkb_CFLAGS = \
+    @XKB_CFLAGS@ \
+    @X11_CFLAGS@ \
+    @GLIB2_CFLAGS@ \
+    $(NULL)
+ibus_xkb_LDADD = \
+    @XKB_LIBS@ \
+    @X11_LIBS@ \
+    @GLIB2_LIBS@ \
+    $(libibus) \
+    $(NULL)
+endif
+
 CLEANFILES = \
 	simple.xml \
 	$(NULL)
diff --git a/engine/ibus-xkb-main.c b/engine/ibus-xkb-main.c
new file mode 100644
index 0000000..75f6679
--- /dev/null
+++ b/engine/ibus-xkb-main.c
@@ -0,0 +1,111 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gi18n.h>
+#include <X11/Xlib.h>
+
+#ifdef ENABLE_NLS
+#include <locale.h>
+#endif
+
+#include "xkblib.h"
+
+static gboolean get_layout = FALSE;
+static gboolean get_group = FALSE;
+static gchar *layout = NULL;
+static gchar *variant = NULL;
+static gchar *option = NULL;
+static int group = 0;
+
+static const GOptionEntry entries[] =
+{
+    { "get", 'g', 0, G_OPTION_ARG_NONE, &get_layout, N_("Get current xkb layout"), NULL },
+    { "layout", 'l', 0, G_OPTION_ARG_STRING, &layout, N_("Set xkb LAYOUT"), N_("LAYOUT") },
+    { "variant", 'v', 0, G_OPTION_ARG_STRING, &variant, N_("Set xkb VARIANT"), N_("VARIANT") },
+    { "option", 'o', 0, G_OPTION_ARG_STRING, &option, N_("Set xkb OPTION"), N_("OPTION") },
+    { "get-group", 'G', 0, G_OPTION_ARG_NONE, &get_group, N_("Get current xkb state"), NULL },
+    { NULL },
+};
+
+int
+main (int argc, char *argv[])
+{
+    GOptionContext *context;
+    GError *error = NULL;
+    Display *xdisplay;
+
+#ifdef ENABLE_NLS
+    setlocale (LC_ALL, "");
+
+    bindtextdomain (GETTEXT_PACKAGE, IBUS_LOCALEDIR);
+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+    context = g_option_context_new ("- ibus daemon");
+
+    g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+
+    if (!g_option_context_parse (context, &argc, &argv, &error)) {
+        g_printerr ("Option parsing failed: %s\n", error->message);
+        return -1;
+    }
+
+    xdisplay = XOpenDisplay (NULL);
+    if (xdisplay == NULL) {
+        g_warning ("Could not open display");
+        return -1;
+    }
+    ibus_xkb_init (xdisplay);
+
+    if (layout) {
+        ibus_xkb_set_layout (layout, variant, option);
+    }
+    if (get_layout) {
+        layout = ibus_xkb_get_current_layout ();
+        variant = ibus_xkb_get_current_variant ();
+        option = ibus_xkb_get_current_option ();
+        g_printf ("layout: %s\n"
+                  "variant: %s\n"
+                  "option: %s\n",
+                  layout ? layout : "",
+                  variant ? variant : "",
+                  option ? option : "");
+        g_free (layout);
+        g_free (variant);
+        g_free (option);
+    }
+    if (get_group) {
+        group = ibus_xkb_get_current_group ();
+        g_printf ("group: %d\n", group);
+    }
+
+    ibus_xkb_finit ();
+
+    return 0;
+}
diff --git a/engine/main.vala b/engine/main.vala
index e1fd129..377fa2f 100644
--- a/engine/main.vala
+++ b/engine/main.vala
@@ -21,6 +21,85 @@
  */
 
 using IBus;
+using GLib;
+
+private void print_xml(string  layout,
+                       string  layout_desc,
+                       string? variant,
+                       string? variant_desc,
+                       string  lang) {
+    string name = "xkb:%s:%s:%s".printf(layout, variant ?? "", lang);
+    string keymap = layout;
+    string desc = layout_desc;
+    string symbol = lang;
+
+    if (variant != null) {
+        keymap = "%s(%s)".printf(layout, variant);
+    }
+
+    if (variant_desc != null) {
+        desc = variant_desc;
+    }
+
+    desc = desc.replace("<", "&lt;").replace(">", "&gt;");
+
+    if (lang.length > 2) {
+        symbol = lang[0:2];
+    }
+
+    string engine = "
+              <engine>
+                        <name>%s</name>
+                        <language>%s</language>
+                        <license>GPL</license>
+                        <author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
+                        <layout>%s</layout>
+                        <longname>%s</longname>
+                        <description>%s</description>
+                        <icon>ibus-keyboard</icon>
+                        <symbol>%s</symbol>
+                        <rank>%d</rank>
+              </engine>".printf(name, lang, keymap, desc, desc, symbol, 99);
+    print (engine);
+}
+
+private void print_component() {
+    IBus.XKBConfigRegistry registry = new IBus.XKBConfigRegistry();
+    GLib.List layouts = registry.layout_list_get_layouts();
+    GLib.List variants;
+    GLib.List langs;
+    string layout_desc;
+    const string header = "<engines>";
+    const string footer = "
+</engines>";
+
+    print (header);
+    for (unowned GLib.List<string> l = layouts; l != null; l = l.next) {
+        variants = registry.layout_list_get_variants(l.data);
+        langs = registry.layout_lang_get_langs(l.data);
+        layout_desc = registry.layout_desc_get_desc(l.data);
+        for (unowned GLib.List<string> lg = langs; lg != null; lg = lg.next) {
+            print_xml(l.data, layout_desc, null, null, lg.data);
+        }
+        for (unowned GLib.List<string> v = variants; v != null; v = v.next) {
+            var l_v = "%s(%s)".printf(l.data, v.data);
+            unowned GLib.List<string> l_v_langs = null;
+            GLib.List<string> _l_v_langs = registry.layout_lang_get_langs(l_v);
+            l_v_langs = _l_v_langs;
+            if (l_v_langs == null) {
+                l_v_langs = langs;
+            }
+            for (unowned GLib.List<string> lg = l_v_langs; lg != null; lg = lg.next) {
+                print_xml(l.data,
+                          layout_desc,
+                          v.data,
+                          registry.variant_desc_get_desc(l_v),
+                          lg.data);
+            }
+        }
+    }
+    print (footer);
+}
 
 class DummyEngine : IBus.EngineSimple {
 }
@@ -28,6 +107,13 @@ class DummyEngine : IBus.EngineSimple {
 public int main(string[] args) {
     IBus.init();
 
+    if (args.length >= 2) {
+        if (args[1] == "--xml" || args[1] == "-x") {
+            print_component();
+            return 0;
+        }
+    }
+
     IBus.Bus bus = new IBus.Bus();
     if (!bus.is_connected()) {
         warning("ibus-daemon does not exist.");
diff --git a/engine/simple.xml.in.in b/engine/simple.xml.in.in
index 25db578..7de949a 100644
--- a/engine/simple.xml.in.in
+++ b/engine/simple.xml.in.in
@@ -7,534 +7,5 @@
 	<license>GPL</license>
 	<homepage>http://code.google.com/p/ibus</homepage>
 	<textdomain>ibus</textdomain>
-	<engines>
-		<engine>
-			<name>xkb:us::eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>us</layout>
-			<longname>English (US)</longname>
-			<description>English (US)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:us:intl:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>us(intl)</layout>
-			<longname>English (US, international with dead keys)</longname>
-			<description>English (US, international with dead keys)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:us:colemak:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>us(colemak)</layout>
-			<longname>English (Colemak)</longname>
-			<description>English (Colemak)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:us:dvorak:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>us(dvorak)</layout>
-			<longname>English (Dvorak)</longname>
-			<description>English (Dvorak)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:us:altgr-intl:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>us(altgr-intl)</layout>
-			<longname>English (international AltGr dead keys)</longname>
-			<description>English (international AltGr dead keys)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:us:altgr-intl:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>us(altgr-intl)</layout>
-			<longname>English (international AltGr dead keys)</longname>
-			<description>English (international AltGr dead keys)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:be::ger</name>
-			<language>ger</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>be</layout>
-			<longname>Belgian</longname>
-			<description>Belgian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:be::nld</name>
-			<language>nld</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>be</layout>
-			<longname>Belgian</longname>
-			<description>Belgian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:be::fra</name>
-			<language>fra</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>be</layout>
-			<longname>Belgian</longname>
-			<description>Belgian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:br::por</name>
-			<language>por</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>br</layout>
-			<longname>Portuguese (Brazil)</longname>
-			<description>Portuguese (Brazil)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:bg::bul</name>
-			<language>bul</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>bg</layout>
-			<longname>Bulgarian</longname>
-			<description>Bulgarian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:bg:phonetic:bul</name>
-			<language>bul</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>bg(phonetic)</layout>
-			<longname>Bulgarian (traditional phonetic)</longname>
-			<description>Bulgarian (traditional phonetic)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ca::fra</name>
-			<language>fra</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ca</layout>
-			<longname>French (Canada)</longname>
-			<description>French (Canada)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ca:eng:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ca(eng)</layout>
-			<longname>English (Canada)</longname>
-			<description>English (Canada)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:hr::scr</name>
-			<language>scr</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>hr</layout>
-			<longname>Croatian</longname>
-			<description>Croatian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:cz::cze</name>
-			<language>cze</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>cz</layout>
-			<longname>Czech</longname>
-			<description>Czech</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:dk::dan</name>
-			<language>dan</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>dk</layout>
-			<longname>Danish</longname>
-			<description>Danish</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ee::est</name>
-			<language>est</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ee</layout>
-			<longname>Estonian</longname>
-			<description>Estonian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:fi::fin</name>
-			<language>fin</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>fi</layout>
-			<longname>Finnish</longname>
-			<description>Finnish</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:fr::fra</name>
-			<language>fra</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>fr</layout>
-			<longname>French</longname>
-			<description>French</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:de::ger</name>
-			<language>ger</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>de</layout>
-			<longname>German</longname>
-			<description>German</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:de:neo:ger</name>
-			<language>ger</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>de(neo)</layout>
-			<longname>German (Neo 2)</longname>
-			<description>German (Neo 2)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:gr::gre</name>
-			<language>gre</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>gr</layout>
-			<longname>Greek</longname>
-			<description>Greek</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:hu::hun</name>
-			<language>hun</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>hu</layout>
-			<longname>Hungarian</longname>
-			<description>Hungarian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:il::heb</name>
-			<language>heb</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>il</layout>
-			<longname>Hebrew</longname>
-			<description>Hebrew</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:it::ita</name>
-			<language>ita</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>it</layout>
-			<longname>Italian</longname>
-			<description>Italian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:jp::jpn</name>
-			<language>jpn</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>jp</layout>
-			<longname>Japanese</longname>
-			<description>Japanese</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:latam::spa</name>
-			<language>spa</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>latam</layout>
-			<longname>Spanish (Latin American)</longname>
-			<description>Spanish (Latin American)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:lt::lit</name>
-			<language>lit</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>lt</layout>
-			<longname>Lithuanian</longname>
-			<description>Lithuanian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:lv:apostrophe:lav</name>
-			<language>lav</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>lv(apostrophe)</layout>
-			<longname>Latvian (apostrophe variant)</longname>
-			<description>Latvian (apostrophe variant)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:pl::pol</name>
-			<language>pol</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>pl</layout>
-			<longname>Polish</longname>
-			<description>Polish</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:pt::por</name>
-			<language>por</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>pt</layout>
-			<longname>Portuguese</longname>
-			<description>Portuguese</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ro::rum</name>
-			<language>rum</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ro</layout>
-			<longname>Romanian</longname>
-			<description>Romanian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ru::rus</name>
-			<language>rus</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ru</layout>
-			<longname>Russian</longname>
-			<description>Russian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ru:phonetic:rus</name>
-			<language>rus</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ru(phonetic)</layout>
-			<longname>Russian (phonetic)</longname>
-			<description>Russian (phonetic)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:rs::srp</name>
-			<language>srp</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>rs</layout>
-			<longname>Serbian</longname>
-			<description>Serbian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:si::slv</name>
-			<language>slv</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>si</layout>
-			<longname>Slovenian</longname>
-			<description>Slovenian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:sk::slo</name>
-			<language>slo</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>sk</layout>
-			<longname>Slovak</longname>
-			<description>Slovak</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:es::spa</name>
-			<language>spa</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>es</layout>
-			<longname>Spanish</longname>
-			<description>Spanish</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:es:cat:cat</name>
-			<language>cat</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>es(cat)</layout>
-			<longname>Catalan (Spain, with middle-dot L)</longname>
-			<description>Catalan (Spain, with middle-dot L)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:se::swe</name>
-			<language>swe</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>se</layout>
-			<longname>Swedish</longname>
-			<description>Swedish</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ch::ger</name>
-			<language>ger</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ch</layout>
-			<longname>German (Switzerland)</longname>
-			<description>German (Switzerland)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ch:fr:fra</name>
-			<language>fra</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ch(fr)</layout>
-			<longname>French (Switzerland)</longname>
-			<description>French (Switzerland)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:tr::tur</name>
-			<language>tur</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>tr</layout>
-			<longname>Turkish</longname>
-			<description>Turkish</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:ua::ukr</name>
-			<language>ukr</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>ua</layout>
-			<longname>Ukrainian</longname>
-			<description>Ukrainian</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:gb:extd:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>gb(extd)</layout>
-			<longname>English (UK, extended WinKeys)</longname>
-			<description>English (UK, extended WinKeys)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:gb:dvorak:eng</name>
-			<language>eng</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>gb(dvorak)</layout>
-			<longname>English (UK, Dvorak)</longname>
-			<description>English (UK, Dvorak)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-		<engine>
-			<name>xkb:kr:kr104:kor</name>
-			<language>kor</language>
-			<license>GPL</license>
-			<author>Peng Huang &lt;shawn.p.huang@gmail.com&gt;</author>
-			<layout>kr(kr104)</layout>
-			<longname>Korean (101/104 key compatible)</longname>
-			<description>Korean (101/104 key compatible)</description>
-                        <icon>ibus-keyboard</icon>
-			<rank>99</rank>
-		</engine>
-	</engines>
+	<engines exec=\"${libexecdir}/ibus-engine-simple --xml\" />
 </component>
diff --git a/engine/xkblib.c b/engine/xkblib.c
new file mode 100644
index 0000000..bb25455
--- /dev/null
+++ b/engine/xkblib.c
@@ -0,0 +1,327 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/XKBlib.h>
+#include <stdio.h> /* for XKBrules.h */
+#include <X11/extensions/XKBrules.h>
+#include <X11/extensions/XKBstr.h>
+#include <string.h>
+
+#include "xkblib.h"
+
+#ifndef XKB_RULES_XML_FILE
+#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
+#endif
+
+static gchar          **default_layouts;
+static gchar          **default_variants;
+static gchar          **default_options;
+static int              default_layout_group;
+
+static Display *
+get_xdisplay (Display *xdisplay)
+{
+    static Display *saved_xdisplay = NULL;
+    if (xdisplay != NULL) {
+        saved_xdisplay = xdisplay;
+    }
+    return saved_xdisplay;
+}
+
+static void
+init_xkb_default_layouts (Display *xdisplay)
+{
+    XkbStateRec state;
+    Atom xkb_rules_name, type;
+    int format;
+    unsigned long l, nitems, bytes_after;
+    unsigned char *prop = NULL;
+
+    xkb_rules_name = XInternAtom (xdisplay, "_XKB_RULES_NAMES", TRUE);
+    if (xkb_rules_name == None) {
+        g_warning ("Could not get XKB rules atom");
+        return;
+    }
+    if (XGetWindowProperty (xdisplay,
+                            XDefaultRootWindow (xdisplay),
+                            xkb_rules_name,
+                            0, 1024, FALSE, XA_STRING,
+                            &type, &format, &nitems, &bytes_after, &prop) != Success) {
+        g_warning ("Could not get X property");
+        return;
+    }
+    if (nitems < 3) {
+        g_warning ("Could not get group layout from X property");
+        return;
+    }
+    for (l = 0; l < 2; l++) {
+        prop += strlen ((const char *) prop) + 1;
+    }
+    if (prop == NULL || *prop == '\0') {
+        g_warning ("No layouts form X property");
+        return;
+    }
+    default_layouts = g_strsplit ((gchar *) prop, ",", -1);
+    prop += strlen ((const char *) prop) + 1;
+    default_variants = g_strsplit ((gchar *) prop, ",", -1);
+    prop += strlen ((const char *) prop) + 1;
+    default_options = g_strsplit ((gchar *) prop, ",", -1);
+
+    if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) {
+        g_warning ("Could not get state");
+        return;
+    }
+    default_layout_group = state.group;
+}
+
+static Bool
+set_xkb_rules (Display *xdisplay,
+               const char *rules_file, const char *model,
+               const char *all_layouts, const char *all_variants,
+               const char *all_options)
+{
+    gchar *rules_path;
+    XkbRF_RulesPtr rules;
+    XkbRF_VarDefsRec rdefs;
+    XkbComponentNamesRec rnames;
+    XkbDescPtr xkb;
+
+    rules_path = g_strdup ("./rules/evdev");
+    rules = XkbRF_Load (rules_path, "C", TRUE, TRUE);
+    if (rules == NULL) {
+        g_return_val_if_fail (XKB_RULES_XML_FILE != NULL, FALSE);
+
+        g_free (rules_path);
+        if (g_str_has_suffix (XKB_RULES_XML_FILE, ".xml")) {
+            rules_path = g_strndup (XKB_RULES_XML_FILE,
+                                    strlen (XKB_RULES_XML_FILE) - 4);
+        } else {
+            rules_path = g_strdup (XKB_RULES_XML_FILE);
+        }
+        rules = XkbRF_Load (rules_path, "C", TRUE, TRUE);
+    }
+    g_return_val_if_fail (rules != NULL, FALSE);
+
+    memset (&rdefs, 0, sizeof (XkbRF_VarDefsRec));
+    memset (&rnames, 0, sizeof (XkbComponentNamesRec));
+    rdefs.model = model ? g_strdup (model) : NULL;
+    rdefs.layout = all_layouts ? g_strdup (all_layouts) : NULL;
+    rdefs.variant = all_variants ? g_strdup (all_variants) : NULL;
+    rdefs.options = all_options ? g_strdup (all_options) : NULL;
+    XkbRF_GetComponents (rules, &rdefs, &rnames);
+    xkb = XkbGetKeyboardByName (xdisplay, XkbUseCoreKbd, &rnames,
+                                XkbGBN_AllComponentsMask,
+                                XkbGBN_AllComponentsMask &
+                                (~XkbGBN_GeometryMask), True);
+    if (!xkb) {
+        g_warning ("Cannot load new keyboard description.");
+        return FALSE;
+    }
+    XkbRF_SetNamesProp (xdisplay, rules_path, &rdefs);
+    g_free (rules_path);
+    g_free (rdefs.model);
+    g_free (rdefs.layout);
+    g_free (rdefs.variant);
+    g_free (rdefs.options);
+
+    return TRUE;
+}
+
+static Bool
+update_xkb_properties (Display *xdisplay,
+                       const char *rules_file, const char *model,
+                       const char *all_layouts, const char *all_variants,
+                       const char *all_options)
+{
+    int len;
+    char *pval;
+    char *next;
+    Atom rules_atom;
+    Window root_window;
+
+    len = (rules_file ? strlen (rules_file) : 0);
+    len += (model ? strlen (model) : 0);
+    len += (all_layouts ? strlen (all_layouts) : 0);
+    len += (all_variants ? strlen (all_variants) : 0);
+    len += (all_options ? strlen (all_options) : 0);
+
+    if (len < 1) {
+        return TRUE;
+    }
+    len += 5; /* trailing NULs */
+
+    rules_atom = XInternAtom (xdisplay, _XKB_RF_NAMES_PROP_ATOM, False);
+    root_window = XDefaultRootWindow (xdisplay);
+    pval = next = g_new0 (char, len + 1);
+    if (!pval) {
+        return TRUE;
+    }
+
+    if (rules_file) {
+        strcpy (next, rules_file);
+        next += strlen (rules_file);
+    }
+    *next++ = '\0';
+    if (model) {
+        strcpy (next, model);
+        next += strlen (model);
+    }
+    *next++ = '\0';
+    if (all_layouts) {
+        strcpy (next, all_layouts);
+        next += strlen (all_layouts);
+    }
+    *next++ = '\0';
+    if (all_variants) {
+        strcpy (next, all_variants);
+        next += strlen (all_variants);
+    }
+    *next++ = '\0';
+    if (all_options) {
+        strcpy (next, all_options);
+        next += strlen (all_options);
+    }
+    *next++ = '\0';
+    if ((next - pval) != len) {
+        g_free (pval);
+        return TRUE;
+    }
+
+    XChangeProperty (xdisplay, root_window,
+                    rules_atom, XA_STRING, 8, PropModeReplace,
+                    (unsigned char *) pval, len);
+    XSync(xdisplay, False);
+
+    return TRUE;
+}
+
+void
+ibus_xkb_init (Display *xdisplay)
+{
+    get_xdisplay (xdisplay);
+    init_xkb_default_layouts (xdisplay);
+}
+
+void
+ibus_xkb_finit (void)
+{
+    g_strfreev (default_layouts);
+    default_layouts = NULL;
+    g_strfreev (default_variants);
+    default_variants = NULL;
+    g_strfreev (default_options);
+    default_options = NULL;
+}
+
+gchar *
+ibus_xkb_get_current_layout (void)
+{
+    if (default_layouts == NULL) {
+        g_warning ("Your system seems not to support XKB.");
+        return NULL;
+    }
+
+    return g_strjoinv (",", (gchar **) default_layouts);
+}
+
+gchar *
+ibus_xkb_get_current_variant (void)
+{
+    if (default_variants == NULL) {
+        return NULL;
+    }
+
+    return g_strjoinv (",", (gchar **) default_variants);
+}
+
+gchar *
+ibus_xkb_get_current_option (void)
+{
+    if (default_options == NULL) {
+        return NULL;
+    }
+
+    return g_strjoinv (",", (gchar **) default_options);
+}
+
+gboolean
+ibus_xkb_set_layout  (const char *layouts,
+                      const char *variants,
+                      const char *options)
+{
+    Display *xdisplay;
+    gboolean retval;
+    gchar *layouts_line;
+
+    if (default_layouts == NULL) {
+        g_warning ("Your system seems not to support XKB.");
+        return FALSE;
+    }
+
+    if (layouts == NULL || g_strcmp0 (layouts, "default") == 0) {
+        layouts_line = g_strjoinv (",", (gchar **) default_layouts);
+    } else {
+        layouts_line = g_strdup (layouts);
+    }
+
+    xdisplay = get_xdisplay (NULL);
+    retval = set_xkb_rules (xdisplay,
+                            "evdev", "evdev",
+                            layouts_line, variants, options);
+    update_xkb_properties (xdisplay,
+                           "evdev", "evdev",
+                           layouts_line, variants, options);
+    g_free (layouts_line);
+
+    return retval;
+}
+
+int
+ibus_xkb_get_current_group (void)
+{
+    Display *xdisplay = get_xdisplay (NULL);
+    XkbStateRec state;
+
+    if (default_layouts == NULL) {
+        g_warning ("Your system seems not to support XKB.");
+        return 0;
+    }
+
+    if (xdisplay == NULL) {
+        g_warning ("ibus-xkb is not initialized.");
+        return 0;
+    }
+
+    if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) != Success) {
+        g_warning ("Could not get state");
+        return 0;
+    }
+
+    return state.group;
+}
diff --git a/engine/xkblib.h b/engine/xkblib.h
new file mode 100644
index 0000000..36597c3
--- /dev/null
+++ b/engine/xkblib.h
@@ -0,0 +1,41 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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 __XKBLIB_H_
+#define __XKBLIB_H_
+
+#include <X11/Xlib.h>
+
+G_BEGIN_DECLS
+
+void             ibus_xkb_init                   (Display *xdisplay);
+void             ibus_xkb_finit                  (void);
+gchar           *ibus_xkb_get_current_layout     (void);
+gchar           *ibus_xkb_get_current_variant    (void);
+gchar           *ibus_xkb_get_current_option     (void);
+gboolean         ibus_xkb_set_layout             (const char *layouts,
+                                                  const char *variants,
+                                                  const char *options);
+int              ibus_xkb_get_current_group      (void);
+
+G_END_DECLS
+#endif
diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in
index 9f593ab..66b902a 100644
--- a/ibus-1.0.pc.in
+++ b/ibus-1.0.pc.in
@@ -1,9 +1,13 @@
 prefix=@prefix@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
+libexecdir=@libexecdir@
 includedir=@includedir@
 datadir=@datadir@
 pkgdatadir=@datadir@/ibus
+have_ibus_xkb=@HAVE_IBUS_XKB@
+ibus_xkb=${libexecdir}/ibus-xkb
+have_ibus_gkbd=@HAVE_IBUS_GKBD@
 
 Name: IBus
 Description: IBus Library
diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py
index 8d1424b..0ac7368 100644
--- a/setup/enginecombobox.py
+++ b/setup/enginecombobox.py
@@ -75,7 +75,11 @@ class EngineComboBox(Gtk.ComboBox):
 
         keys = langs.keys()
         keys.sort(locale.strcoll)
-        current_lang = IBus.get_language_name(locale.getlocale()[0])
+        loc = locale.getlocale()[0]
+        # None on C locale
+        if loc == None:
+            loc = 'en_US'
+        current_lang = IBus.get_language_name(loc)
         # move current language to the first place
         if current_lang in keys:
             keys.remove(current_lang)
diff --git a/setup/main.py b/setup/main.py
index 274b25a..e3027a5 100644
--- a/setup/main.py
+++ b/setup/main.py
@@ -182,12 +182,25 @@ class Setup(object):
                 self.__checkbutton_use_global_engine_toggled_cb)
 
         # init engine page
-        self.__engines = self.__bus.list_engines()
+        self.__engines = []
+        value = self.__config.get_value("general", "load_xkb_layouts")
+        load_layouts = []
+        if value != None:
+            load_layouts = map(lambda l: str(l), list(value))
+        if len(load_layouts) == 0:
+            self.__engines = self.__bus.list_engines()
+        else:
+            for engine in self.__bus.list_engines():
+                if not engine.props.name.startswith('xkb:'):
+                    self.__engines.append(engine)
+                elif engine.props.layout in load_layouts:
+                    self.__engines.append(engine)
+
         self.__combobox = self.__builder.get_object("combobox_engines")
         self.__combobox.set_engines(self.__engines)
 
         tmp_dict = {}
-        for e in self.__engines:
+        for e in self.__bus.list_engines():
             tmp_dict[e.get_name()] = e
         engine_names = values.get("preload_engines", [])
         engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
diff --git a/src/Makefile.am b/src/Makefile.am
index b4d0dcf..1631cae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -190,6 +190,11 @@ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
 CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA)
 endif
 
+if ENABLE_XKB
+ibus_sources += ibusxkbxml.c
+ibus_headers += ibusxkbxml.h
+endif
+
 # gen enum types
 ibusenumtypes.h: $(ibus_headers) ibusenumtypes.h.template
 	$(AM_V_GEN) ( top_builddir=`cd $(top_builddir) && pwd`; \
diff --git a/src/ibus.h b/src/ibus.h
index ef811a4..f82a162 100644
--- a/src/ibus.h
+++ b/src/ibus.h
@@ -47,6 +47,7 @@
 #include <ibuskeys.h>
 #include <ibusenumtypes.h>
 #include <ibushotkey.h>
+#include <ibusxkbxml.h>
 #include <ibusxml.h>
 #include <ibusenginedesc.h>
 #include <ibusobservedpath.h>
diff --git a/src/ibusxkbxml.c b/src/ibusxkbxml.c
new file mode 100644
index 0000000..4792664
--- /dev/null
+++ b/src/ibusxkbxml.c
@@ -0,0 +1,466 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "ibus.h"
+#include "ibusxkbxml.h"
+
+#ifndef XKB_RULES_XML_FILE
+#define XKB_RULES_XML_FILE "/usr/share/X11/xkb/rules/evdev.xml"
+#endif
+
+#define IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE(o)  \
+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryPrivate))
+
+typedef struct _IBusXKBConfigRegistryPrivate IBusXKBConfigRegistryPrivate;
+
+struct _IBusXKBConfigRegistryPrivate {
+    GHashTable *layout_list;
+    GHashTable *layout_lang;
+    GHashTable *layout_desc;
+    GHashTable *variant_desc;
+};
+
+
+/* functions prototype */
+static void         ibus_xkb_config_registry_destroy
+                                           (IBusXKBConfigRegistry *xkb_config);
+
+G_DEFINE_TYPE (IBusXKBConfigRegistry, ibus_xkb_config_registry, IBUS_TYPE_OBJECT)
+
+static void
+parse_xkb_xml_languagelist_node (IBusXKBConfigRegistryPrivate *priv,
+                                 XMLNode *parent_node,
+                                 const gchar *layout_name)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+    GList *lang_list = NULL;
+
+    g_assert (node != NULL);
+    g_assert (layout_name != NULL);
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "iso639Id") == 0) {
+            lang_list = g_list_append (lang_list,
+                                       (gpointer) g_strdup (sub_node->text));
+            continue;
+        }
+    }
+    if (lang_list == NULL) {
+        /* some nodes have no lang */
+        return;
+    }
+    if (g_hash_table_lookup (priv->layout_lang, layout_name) != NULL) {
+        g_warning ("duplicated name %s exists", layout_name);
+        return;
+    }
+    g_hash_table_insert (priv->layout_lang,
+                         (gpointer) g_strdup (layout_name),
+                         (gpointer) lang_list);
+}
+
+static const gchar *
+parse_xkb_xml_configitem_node (IBusXKBConfigRegistryPrivate *priv,
+                               XMLNode *parent_node)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+    gchar *name = NULL;
+    gchar *description = NULL;
+
+    g_assert (node != NULL);
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "name") == 0) {
+            name = sub_node->text;
+            continue;
+        }
+        if (g_strcmp0 (sub_node->name, "description") == 0) {
+            description = sub_node->text;
+            continue;
+        }
+        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
+            if (name == NULL) {
+                g_warning ("layout name is NULL in node %s", node->name);
+                continue;
+            }
+            parse_xkb_xml_languagelist_node (priv, sub_node, name);
+            continue;
+        }
+    }
+    if (name == NULL) {
+        g_warning ("No name in layout node");
+        return NULL;
+    }
+    if (g_hash_table_lookup (priv->layout_desc, name) != NULL) {
+        g_warning ("duplicated name %s exists", name);
+        return name;
+    }
+    g_hash_table_insert (priv->layout_desc,
+                         (gpointer) g_strdup (name),
+                         (gpointer) g_strdup (description));
+
+    return name;
+}
+
+static const gchar *
+parse_xkb_xml_variant_configitem_node (IBusXKBConfigRegistryPrivate *priv,
+                            XMLNode *parent_node,
+                            const gchar *layout_name)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+    gchar *name = NULL;
+    gchar *description = NULL;
+    gchar *variant_lang_name = NULL;
+
+    g_assert (node != NULL);
+    g_assert (layout_name != NULL);
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "name") == 0) {
+            name = sub_node->text;
+            continue;
+        }
+        if (g_strcmp0 (sub_node->name, "description") == 0) {
+            description = sub_node->text;
+            continue;
+        }
+        if (g_strcmp0 (sub_node->name, "languageList") == 0) {
+            if (name == NULL) {
+                g_warning ("layout name is NULL in node %s", node->name);
+                continue;
+            }
+            variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
+            parse_xkb_xml_languagelist_node (priv, sub_node, variant_lang_name);
+            g_free (variant_lang_name);
+            continue;
+        }
+    }
+    if (name == NULL) {
+        g_warning ("No name in layout node");
+        return NULL;
+    }
+    if (g_hash_table_lookup (priv->variant_desc, name) != NULL) {
+        /* This is an expected case. */
+        return name;
+    }
+    variant_lang_name = g_strdup_printf ("%s(%s)", layout_name, name);
+    g_hash_table_insert (priv->variant_desc,
+                         (gpointer) variant_lang_name,
+                         (gpointer) g_strdup (description));
+    return name;
+}
+
+static const gchar *
+parse_xkb_xml_variant_node (IBusXKBConfigRegistryPrivate *priv,
+                            XMLNode *parent_node,
+                            const gchar *layout_name)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+    const gchar *variant_name = NULL;
+
+    g_assert (node != NULL);
+    g_assert (layout_name != NULL);
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
+            variant_name = parse_xkb_xml_variant_configitem_node (priv, sub_node, layout_name);
+            continue;
+        }
+    }
+    return variant_name;
+}
+
+static GList *
+parse_xkb_xml_variantlist_node (IBusXKBConfigRegistryPrivate *priv,
+                                XMLNode *parent_node,
+                                const gchar *layout_name,
+                                GList *variant_list)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+    const gchar *variant_name = NULL;
+
+    g_assert (node != NULL);
+    g_assert (layout_name != NULL);
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "variant") == 0) {
+            variant_name = parse_xkb_xml_variant_node (priv, sub_node, layout_name);
+            if (variant_name != NULL) {
+                variant_list = g_list_append (variant_list,
+                                              (gpointer) g_strdup (variant_name));
+            }
+            continue;
+        }
+    }
+    return variant_list;
+}
+
+static void
+parse_xkb_xml_layout_node (IBusXKBConfigRegistryPrivate *priv,
+                           XMLNode *parent_node)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+    const gchar *name = NULL;
+    GList *variant_list = NULL;
+
+    g_assert (node != NULL);
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "configItem") == 0) {
+            name = parse_xkb_xml_configitem_node (priv, sub_node);
+            continue;
+        }
+        if (g_strcmp0 (sub_node->name, "variantList") == 0) {
+            if (name == NULL) {
+                g_warning ("layout name is NULL in node %s", node->name);
+                continue;
+            }
+            variant_list = parse_xkb_xml_variantlist_node (priv, sub_node,
+                                                           name,
+                                                           variant_list);
+            continue;
+        }
+    }
+    if (g_hash_table_lookup (priv->layout_list, name) != NULL) {
+        g_warning ("duplicated name %s exists", name);
+        return;
+    }
+    g_hash_table_insert (priv->layout_list,
+                         (gpointer) g_strdup (name),
+                         (gpointer) variant_list);
+}
+
+static void
+parse_xkb_xml_top_node (IBusXKBConfigRegistryPrivate *priv,
+                        XMLNode *parent_node)
+{
+    XMLNode *node = parent_node;
+    XMLNode *sub_node;
+    GList *p;
+
+    g_assert (priv != NULL);
+    g_assert (node != NULL);
+
+    if (g_strcmp0 (node->name, "xkbConfigRegistry") != 0) {
+        g_warning ("node has no xkbConfigRegistry name");
+        return;
+    }
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "layoutList") == 0) {
+            break;
+        }
+    }
+    if (p == NULL) {
+        g_warning ("xkbConfigRegistry node has no layoutList node");
+        return;
+    }
+    node = sub_node;
+    for (p = node->sub_nodes; p; p = p->next) {
+        sub_node = (XMLNode *) p->data;
+        if (g_strcmp0 (sub_node->name, "layout") == 0) {
+            parse_xkb_xml_layout_node (priv, sub_node);
+            continue;
+        }
+    }
+}
+
+static void
+free_lang_list (GList *list)
+{
+    GList *l = list;
+    while (l) {
+        g_free (l->data);
+        l->data = NULL;
+        l = l->next;
+    }
+    g_list_free (list);
+}
+
+static void
+parse_xkb_config_registry_file (IBusXKBConfigRegistryPrivate *priv,
+                                const gchar *file)
+{
+    XMLNode *node;
+
+    g_assert (file != NULL);
+
+    priv->layout_list = g_hash_table_new_full (g_str_hash,
+                                               (GEqualFunc) g_str_equal,
+                                               (GDestroyNotify) g_free,
+                                               (GDestroyNotify) free_lang_list);
+    priv->layout_desc = g_hash_table_new_full (g_str_hash,
+                                               (GEqualFunc) g_str_equal,
+                                               (GDestroyNotify) g_free,
+                                               (GDestroyNotify) g_free);
+    priv->layout_lang = g_hash_table_new_full (g_str_hash,
+                                               (GEqualFunc) g_str_equal,
+                                               (GDestroyNotify) g_free,
+                                               (GDestroyNotify) free_lang_list);
+    priv->variant_desc = g_hash_table_new_full (g_str_hash,
+                                               (GEqualFunc) g_str_equal,
+                                               (GDestroyNotify) g_free,
+                                               (GDestroyNotify) g_free);
+    node = ibus_xml_parse_file (file);
+    parse_xkb_xml_top_node (priv, node);
+    ibus_xml_free (node);
+}
+
+static void
+ibus_xkb_config_registry_init (IBusXKBConfigRegistry *xkb_config)
+{
+    IBusXKBConfigRegistryPrivate *priv;
+    const gchar *file = XKB_RULES_XML_FILE;
+
+    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
+    parse_xkb_config_registry_file (priv, file);
+}
+
+static void
+ibus_xkb_config_registry_destroy (IBusXKBConfigRegistry *xkb_config)
+{
+    IBusXKBConfigRegistryPrivate *priv;
+
+    g_return_if_fail (xkb_config != NULL);
+
+    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);
+
+    g_hash_table_destroy (priv->layout_list);
+    priv->layout_list = NULL;
+    g_hash_table_destroy (priv->layout_lang);
+    priv->layout_lang= NULL;
+    g_hash_table_destroy (priv->layout_desc);
+    priv->layout_desc= NULL;
+    g_hash_table_destroy (priv->variant_desc);
+    priv->variant_desc = NULL;
+
+    IBUS_OBJECT_CLASS(ibus_xkb_config_registry_parent_class)->destroy (IBUS_OBJECT (xkb_config));
+}
+
+static void
+ibus_xkb_config_registry_class_init (IBusXKBConfigRegistryClass *klass)
+{
+    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
+
+    g_type_class_add_private (klass, sizeof (IBusXKBConfigRegistryPrivate));
+
+    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_xkb_config_registry_destroy;
+}
+
+IBusXKBConfigRegistry *
+ibus_xkb_config_registry_new (void)
+{
+    IBusXKBConfigRegistry *xkb_config;
+
+    xkb_config = IBUS_XKB_CONFIG_REGISTRY (g_object_new (IBUS_TYPE_XKB_CONFIG_REGISTRY, NULL));
+    return xkb_config;
+}
+
+#define TABLE_FUNC(field_name) const GHashTable *                       \
+ibus_xkb_config_registry_get_##field_name  (IBusXKBConfigRegistry *xkb_config) \
+{                                                                       \
+    IBusXKBConfigRegistryPrivate *priv;                                 \
+                                                                        \
+    g_return_val_if_fail (xkb_config != NULL, NULL);                    \
+    priv = IBUS_XKB_CONFIG_REGISTRY_GET_PRIVATE (xkb_config);           \
+    return priv->field_name;                                            \
+}
+
+TABLE_FUNC (layout_list)
+TABLE_FUNC (layout_lang)
+TABLE_FUNC (layout_desc)
+TABLE_FUNC (variant_desc)
+
+#undef TABLE_FUNC
+
+GList *
+ibus_xkb_config_registry_layout_list_get_layouts (IBusXKBConfigRegistry *xkb_config)
+{
+    GHashTable *table;
+    GList *list = NULL;
+
+    table = (GHashTable *)
+        ibus_xkb_config_registry_get_layout_list (xkb_config);
+    list = (GList *) g_hash_table_get_keys (table);
+    return list;
+}
+
+/* vala could use GLib.List<string> for the returned pointer and
+ * the declaration calls g_list_foreach (retval, g_free, NULL).
+ * When I think about GLib.List<string> v.s. GLib.List, probably
+ * I think GLib.List<string> is better for the function and set
+ * g_strdup() here. I do not know about GJS implementation.
+ */
+#define TABLE_LOOKUP_LIST_FUNC(field_name, value) GList *               \
+ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
+{                                                                       \
+    GHashTable *table;                                                  \
+    GList *list = NULL;                                                 \
+    GList *retval= NULL;                                                \
+    GList *p = NULL;                                                    \
+                                                                        \
+    table = (GHashTable *)                                              \
+        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
+    list = (GList *) g_hash_table_lookup (table, key);                  \
+    retval = g_list_copy (list);                                        \
+    for (p = retval; p; p = p->next) {                                  \
+        p->data = g_strdup (p->data);                                   \
+    }                                                                   \
+    return retval;                                                      \
+}
+
+#define TABLE_LOOKUP_STRING_FUNC(field_name, value) gchar *             \
+ibus_xkb_config_registry_##field_name##_get_##value  (IBusXKBConfigRegistry *xkb_config, const gchar *key) \
+{                                                                       \
+    GHashTable *table;                                                  \
+    const gchar *desc = NULL;                                           \
+                                                                        \
+    table = (GHashTable *)                                              \
+        ibus_xkb_config_registry_get_##field_name (xkb_config);         \
+    desc = (const gchar *) g_hash_table_lookup (table, key);            \
+    return g_strdup (desc);                                             \
+}
+
+TABLE_LOOKUP_LIST_FUNC (layout_list, variants)
+TABLE_LOOKUP_LIST_FUNC (layout_lang, langs)
+TABLE_LOOKUP_STRING_FUNC (layout_desc, desc)
+TABLE_LOOKUP_STRING_FUNC (variant_desc, desc)
+
+#undef TABLE_LOOKUP_LIST_FUNC
+#undef TABLE_LOOKUP_STRING_FUNC
diff --git a/src/ibusxkbxml.h b/src/ibusxkbxml.h
new file mode 100644
index 0000000..6f5b7bd
--- /dev/null
+++ b/src/ibusxkbxml.h
@@ -0,0 +1,187 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* vim:set et sts=4: */
+/* bus - The Input Bus
+ * Copyright (C) 2012 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * 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 __IBUS_XKBXML_H_
+#define __IBUS_XKBXML_H_
+
+#if !defined (__IBUS_H_INSIDE__) && !defined (IBUS_COMPILATION)
+#error "Only <ibus.h> can be included directly"
+#endif
+
+#include "ibus.h"
+
+/*
+ * Type macros.
+ */
+/* define IBusXKBConfigRegistry macros */
+#define IBUS_TYPE_XKB_CONFIG_REGISTRY                   \
+    (ibus_xkb_config_registry_get_type ())
+#define IBUS_XKB_CONFIG_REGISTRY(obj)                   \
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistry))
+#define IBUS_XKB_CONFIG_REGISTRY_CLASS(klass)           \
+    (G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
+#define IBUS_IS_XKB_CONFIG_REGISTRY(obj)                \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY))
+#define IBUS_IS_XKB_CONFIG_REGISTRY_CLASS(klass)        \
+    (G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_XKB_CONFIG_REGISTRY))
+#define IBUS_XKB_CONFIG_REGISTRY_GET_CLASS(obj)         \
+    (G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_XKB_CONFIG_REGISTRY, IBusXKBConfigRegistryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _IBusXKBConfigRegistry IBusXKBConfigRegistry;
+typedef struct _IBusXKBConfigRegistryClass IBusXKBConfigRegistryClass;
+
+struct _IBusXKBConfigRegistry {
+    IBusObject parent;
+};
+
+struct _IBusXKBConfigRegistryClass {
+    IBusObjectClass parent;
+    /* signals */
+    /*< private >*/
+    /* padding */
+    gpointer pdummy[8];
+};
+
+
+GType            ibus_xkb_config_registry_get_type
+                                                 (void);
+
+/**
+ * ibus_xkb_config_registry_new:
+ * @returns: A newly allocated IBusXKBConfigRegistry
+ *
+ * New an IBusXKBConfigRegistry.
+ */
+IBusXKBConfigRegistry *
+                 ibus_xkb_config_registry_new
+                                                 (void);
+
+/**
+ * ibus_xkb_config_registry_get_layout_list: (skip)
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @returns: A const GHashTable
+ *
+ * a const GHashTable
+ */
+const GHashTable *
+                 ibus_xkb_config_registry_get_layout_list
+                                                 (IBusXKBConfigRegistry *xkb_config);
+
+/**
+ * ibus_xkb_config_registry_get_layout_lang: (skip)
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @returns: A const GHashTable
+ *
+ * a const GHashTable
+ */
+const GHashTable *
+                 ibus_xkb_config_registry_get_layout_lang
+                                                 (IBusXKBConfigRegistry *xkb_config);
+
+/**
+ * ibus_xkb_config_registry_get_layout_desc: (skip)
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @returns: A const GHashTable
+ *
+ * a const GHashTable
+ */
+const GHashTable *
+                 ibus_xkb_config_registry_get_layout_desc
+                                                 (IBusXKBConfigRegistry *xkb_config);
+
+/**
+ * ibus_xkb_config_registry_get_variant_desc: (skip)
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @returns: A const GHashTable
+ *
+ * a const GHashTable
+ */
+const GHashTable *
+                 ibus_xkb_config_registry_get_variant_desc
+                                                 (IBusXKBConfigRegistry *xkb_config);
+
+/**
+ * ibus_xkb_config_registry_layout_list_get_layouts:
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @returns: (transfer container) (element-type utf8): A GList of layouts
+ *
+ * a GList of layouts
+ */
+GList *
+                 ibus_xkb_config_registry_layout_list_get_layouts
+                                                 (IBusXKBConfigRegistry *xkb_config);
+
+/**
+ * ibus_xkb_config_registry_layout_list_get_variants:
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @layout: A layout.
+ * @returns: (transfer container) (element-type utf8): A GList
+ *
+ * a GList
+ */
+GList *
+                 ibus_xkb_config_registry_layout_list_get_variants
+                                                 (IBusXKBConfigRegistry *xkb_config,
+                                                  const gchar           *layout);
+
+/**
+ * ibus_xkb_config_registry_layout_lang_get_langs:
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @layout: A layout.
+ * @returns: (transfer container) (element-type utf8): A GList
+ *
+ * a GList
+ */
+GList *
+                 ibus_xkb_config_registry_layout_lang_get_langs
+                                                 (IBusXKBConfigRegistry *xkb_config,
+                                                  const gchar           *layout);
+
+/**
+ * ibus_xkb_config_registry_layout_desc_get_desc:
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @layout: A layout.
+ * @returns: A layout description
+ *
+ * a layout description
+ */
+gchar *
+                 ibus_xkb_config_registry_layout_desc_get_desc
+                                                 (IBusXKBConfigRegistry *xkb_config,
+                                                  const gchar           *layout);
+
+/**
+ * ibus_xkb_config_registry_variant_desc_get_desc:
+ * @xkb_config: An IBusXKBConfigRegistry.
+ * @variant: A variant.
+ * @returns: A variant description
+ *
+ * a variant description
+ */
+gchar *
+                 ibus_xkb_config_registry_variant_desc_get_desc
+                                                 (IBusXKBConfigRegistry *xkb_config,
+                                                  const gchar           *variant);
+G_END_DECLS
+#endif
diff --git a/ui/gtk3/Gkbd-3.0.metadata b/ui/gtk3/Gkbd-3.0.metadata
new file mode 100644
index 0000000..661e6fd
--- /dev/null
+++ b/ui/gtk3/Gkbd-3.0.metadata
@@ -0,0 +1 @@
+Configuration cheader_filename="libgnomekbd/gkbd-configuration.h"
diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
index 0fb9d3c..33aaaa3 100644
--- a/ui/gtk3/Makefile.am
+++ b/ui/gtk3/Makefile.am
@@ -42,6 +42,9 @@ INCLUDES = \
 	-I$(top_builddir)/src \
 	$(NULL)
 
+HAVE_IBUS_XKB_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_XKB))))
+HAVE_IBUS_GKBD_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_GKBD))))
+
 AM_CFLAGS = \
 	@GLIB2_CFLAGS@ \
 	@GIO2_CFLAGS@ \
@@ -56,6 +59,10 @@ AM_CFLAGS = \
 	-DBINDIR=\"@bindir@\" \
     -DIBUS_DISABLE_DEPRECATED \
 	-DIBUS_VERSION=\"@IBUS_VERSION@\" \
+	-DHAVE_IBUS_XKB=$(HAVE_IBUS_XKB_C) \
+	-DHAVE_IBUS_GKBD=$(HAVE_IBUS_GKBD_C) \
+	-DIBUS_XKB_COMMAND=\"$(libexecdir)/ibus-xkb\" \
+	-DXKB_LAYOUTS_MAX_LENGTH=4 \
 	-Wno-unused-variable \
 	-Wno-unused-but-set-variable \
 	-Wno-unused-function \
@@ -86,6 +93,7 @@ ibus_ui_gtk3_valas = \
 	application.vala \
 	candidatearea.vala \
 	candidatepanel.vala \
+	gkbdlayout.vala \
 	handle.vala \
 	iconwidget.vala \
 	keybindingmanager.vala \
@@ -94,6 +102,7 @@ ibus_ui_gtk3_valas = \
 	property.vala \
 	separator.vala \
 	switcher.vala \
+	xkblayout.vala \
 	$(NULL)
 ibus_ui_gtk3_vala_cfiles = $(ibus_ui_gtk3_valas:.vala=.c)
 
@@ -106,12 +115,48 @@ ibus_ui_gtk3_LDADD = \
 	$(AM_LDADD) \
 	$(NULL)
 
+if ENABLE_LIBGNOMEKBD
+AM_CFLAGS += \
+	@LIBGNOMEKBDUI_CFLAGS@ \
+	@ATK_CFLAGS@ \
+	$(NULL)
+
+AM_LDADD += \
+	@LIBGNOMEKBDUI_LIBS@ \
+	@ATK_LIBS@ \
+	$(NULL)
+
+AM_VALAFLAGS += \
+	--vapidir=. \
+	--pkg=glib-2.0 \
+	--pkg=gmodule-2.0 \
+	--pkg=gkbd \
+	$(NULL)
+
+$(srcdir)/gkbd.vapi:
+	$(VAPIGEN) --metadatadir . --library gkbd \
+	    --pkg gtk+-3.0 --pkg glib-2.0 --pkg gmodule-2.0 \
+	    /usr/share/gir-1.0/Gkbd-3.0.gir
+	$(NULL)
+
+$(srcdir)/gkbdlayout.vala: $(srcdir)/gkbd.vapi
+	@cp $(srcdir)/gkbdlayout.vala.true $(srcdir)/gkbdlayout.vala
+else
+$(srcdir)/gkbdlayout.vala:
+	@cp $(srcdir)/gkbdlayout.vala.false $(srcdir)/gkbdlayout.vala
+endif
+
 CLEANFILES = \
+	gkbd.vapi \
+	gkbdlayout.vala \
 	gtkpanel.xml \
 	$(ibus_ui_gtk3_vala_cfiles) \
 	$(NULL)
 
 EXTRA_DIST = \
+	Gkbd-3.0.metadata \
+	gkbdlayout.vala.false \
+	gkbdlayout.vala.true \
 	gtkpanel.xml.in.in \
 	$(NULL)
 
diff --git a/ui/gtk3/gkbdlayout.vala.false b/ui/gtk3/gkbdlayout.vala.false
new file mode 100644
index 0000000..a387de9
--- /dev/null
+++ b/ui/gtk3/gkbdlayout.vala.false
@@ -0,0 +1,63 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright(c) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2012 Takao Fujiwara <tfujiwar@redhat.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
+ */
+
+public class GkbdLayout
+{
+    public signal void changed();
+    public signal void group_changed (int object);
+
+    public GkbdLayout() {
+    }
+
+    public string[] get_layouts() {
+        return new string[0];
+    }
+
+    public string[] get_group_names() {
+        return new string[0];
+    }
+
+    public void lock_group(int id) {
+    }
+
+    public void start_listen() {
+    }
+
+    public void stop_listen() {
+    }
+
+    /*
+    public static int main(string[] args) {
+        GkbdLayout ibus_layouts = new GkbdLayout();
+
+        string[] layouts = ibus_layouts.get_layouts();
+        string[] names = ibus_layouts.get_group_names();
+        for (int i = 0; layouts != null && i < layouts.length; i++) {
+            stdout.printf("%s %s\n", layouts[i], names[i]);
+        }
+
+        return 0;
+    }
+    */
+}
diff --git a/ui/gtk3/gkbdlayout.vala.true b/ui/gtk3/gkbdlayout.vala.true
new file mode 100644
index 0000000..adacd81
--- /dev/null
+++ b/ui/gtk3/gkbdlayout.vala.true
@@ -0,0 +1,111 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright(c) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2012 Takao Fujiwara <tfujiwar@redhat.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
+ */
+
+using Gkbd;
+using Gtk;
+
+public class GkbdLayout
+{
+    public signal void changed();
+    public signal void group_changed (int object);
+
+    private Gkbd.Configuration m_config = null;
+
+    public GkbdLayout() {
+        m_config = Gkbd.Configuration.get();
+        if (m_config != null) {
+            m_config.changed.connect(config_changed_cb);
+            m_config.group_changed.connect(config_group_changed_cb);
+        }
+    }
+
+    ~GkbdLayout() {
+        if (m_config != null) {
+            m_config.changed.disconnect(config_changed_cb);
+            m_config.group_changed.disconnect(config_group_changed_cb);
+            /* gkbd_configuration_get reuses the object and do not
+             * destroy m_config here. */
+            m_config.ref();
+            m_config = null;
+        }
+    }
+
+    private void config_changed_cb() {
+        changed();
+    }
+
+    private void config_group_changed_cb(int object) {
+        group_changed(object);
+    }
+
+    public string[] get_layouts() {
+        if (m_config == null) {
+            return new string[0];
+        }
+        return m_config.get_short_group_names();
+    }
+
+    public string[] get_group_names() {
+        if (m_config == null) {
+            return new string[0];
+        }
+        return m_config.get_group_names();
+    }
+
+    public void lock_group(int id) {
+        if (m_config == null) {
+            return;
+        }
+        m_config.lock_group(id);
+    }
+
+    public void start_listen() {
+        if (m_config == null) {
+            return;
+        }
+        m_config.start_listen();
+    }
+
+    public void stop_listen() {
+        if (m_config == null) {
+            return;
+        }
+        m_config.stop_listen();
+    }
+
+    /*
+    public static int main(string[] args) {
+        Gtk.init(ref args);
+        GkbdLayout ibus_layouts = new GkbdLayout();
+
+        string[] layouts = ibus_layouts.get_layouts();
+        string[] names = ibus_layouts.get_group_names();
+        for (int i = 0; layouts != null && i < layouts.length; i++) {
+            stdout.printf("%s %s\n", layouts[i], names[i]);
+        }
+
+        return 0;
+    }
+    */
+}
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
index b83d7e3..2a386a0 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -40,8 +40,16 @@ class Panel : IBus.PanelService {
     private PropertyManager m_property_manager;
     private GLib.Pid m_setup_pid = 0;
     private Gtk.AboutDialog m_about_dialog;
+    private GkbdLayout m_gkbdlayout = null;
+    private XKBLayout m_xkblayout = null;
+    private string[] m_layouts = {};
+    private string[] m_variants = {};
+    private int m_fallback_lock_id = -1;
+    private bool m_changed_xkb_option = false;
+    private GLib.Timer m_changed_layout_timer;
     private const string ACCELERATOR_SWITCH_IME_FOREWARD = "<Control>space";
     private const string ACCELERATOR_SWITCH_IME_BACKWARD = "<Control><Shift>space";
+    private string[] ACCELERATOR_IME_HOTKEYS = {};
 
     public Panel(IBus.Bus bus) {
         GLib.assert(bus.is_connected());
@@ -65,13 +73,6 @@ class Panel : IBus.PanelService {
 
         m_switcher = new Switcher();
 
-        var keybinding_manager = KeybindingManager.get_instance();
-        keybinding_manager.bind(ACCELERATOR_SWITCH_IME_FOREWARD,
-                (e) => handle_engine_switch(e, false));
-
-        keybinding_manager.bind(ACCELERATOR_SWITCH_IME_BACKWARD,
-                (e) => handle_engine_switch(e, true));
-
         m_property_manager = new PropertyManager();
         m_property_manager.property_activate.connect((k, s) => {
             property_activate(k, s);
@@ -82,8 +83,66 @@ class Panel : IBus.PanelService {
 
     ~Panel() {
         var keybinding_manager = KeybindingManager.get_instance();
-        keybinding_manager.unbind(ACCELERATOR_SWITCH_IME_FOREWARD);
-        keybinding_manager.unbind(ACCELERATOR_SWITCH_IME_BACKWARD);
+        foreach (string keybinding in ACCELERATOR_IME_HOTKEYS) {
+            keybinding_manager.unbind(keybinding);
+            if (keybinding == ACCELERATOR_SWITCH_IME_FOREWARD) {
+                keybinding_manager.unbind(ACCELERATOR_SWITCH_IME_BACKWARD);
+            }
+        }
+
+        if (HAVE_IBUS_GKBD && m_gkbdlayout != null) {
+            m_gkbdlayout.changed.disconnect(gkbdlayout_changed_cb);
+            m_gkbdlayout.stop_listen();
+            m_gkbdlayout = null;
+        }
+
+        m_xkblayout = null;
+    }
+
+    // ToDo: Customize the input method with ibus-setup
+    private void set_keybinding() {
+        string locale = GLib.Intl.setlocale(GLib.LocaleCategory.ALL,
+                                            null);
+        if (locale == null) {
+            locale = "C";
+        }
+
+        ACCELERATOR_IME_HOTKEYS += ACCELERATOR_SWITCH_IME_FOREWARD;
+
+        if (m_config != null) {
+            GLib.Variant variant = m_config.get_value("general/hotkey",
+                                                      "trigger_accel");
+            if (variant != null) {
+                ACCELERATOR_IME_HOTKEYS = {};
+                for (int i = 0; i < variant.n_children(); i++) {
+                    ACCELERATOR_IME_HOTKEYS += variant.get_child_value(i).dup_string();
+                }
+            }
+        }
+
+        if (ACCELERATOR_IME_HOTKEYS.length == 1 &&
+            ACCELERATOR_IME_HOTKEYS[0] == ACCELERATOR_SWITCH_IME_FOREWARD) {
+            // FIXME: When us keyboard is used, Zenkaku_Hankaku does not work.
+            /*
+            if (locale[0:2] == "ja") {
+                ACCELERATOR_IME_HOTKEYS += "Zenkaku_Hankaku";
+            }
+            */
+            if (locale[0:2] == "ko") {
+                ACCELERATOR_IME_HOTKEYS += "Hangul";
+                ACCELERATOR_IME_HOTKEYS += "Alt_R";
+            }
+        }
+
+        var keybinding_manager = KeybindingManager.get_instance();
+        foreach (string keybinding in ACCELERATOR_IME_HOTKEYS) {
+            keybinding_manager.bind(keybinding,
+                (e) => handle_engine_switch(e, false));
+            if (keybinding == ACCELERATOR_SWITCH_IME_FOREWARD) {
+                keybinding_manager.bind(ACCELERATOR_SWITCH_IME_BACKWARD,
+                    (e) => handle_engine_switch(e, true));
+            }
+        }
     }
 
     public void set_config(IBus.Config config) {
@@ -93,8 +152,10 @@ class Panel : IBus.PanelService {
         }
 
         m_config = config;
+        set_keybinding();
         if (m_config != null) {
             m_config.value_changed.connect(config_value_changed_cb);
+            init_engines_order();
             update_engines(m_config.get_value("general", "preload_engines"),
                            m_config.get_value("general", "engines_order"));
         } else {
@@ -102,6 +163,192 @@ class Panel : IBus.PanelService {
         }
     }
 
+    private void gkbdlayout_changed_cb() {
+        /* The callback is called four times after set_layout is called
+         * so check the elapsed and take the first signal only. */
+        double elapsed = m_changed_layout_timer.elapsed();
+        if (elapsed < 1.0 && elapsed > 0.0) {
+            return;
+        }
+
+        if (m_fallback_lock_id != -1) {
+            /* Call lock_group only when set_layout is called. */
+            m_gkbdlayout.lock_group(m_fallback_lock_id);
+            m_fallback_lock_id = -1;
+        } else {
+            /* Reset default layout when gnome-control-center is called. */
+            m_xkblayout.reset_layout();
+        }
+
+        update_xkb_engines();
+        m_changed_layout_timer.reset();
+    }
+
+    private void init_gkbd() {
+        m_gkbdlayout = new GkbdLayout();
+        m_gkbdlayout.changed.connect(gkbdlayout_changed_cb);
+
+        /* Probably we cannot support both keyboard and ibus indicators
+         * How can I get the engine from keymap of group_id?
+         * e.g. 'en' could be owned by xkb:en and pinyin engines. */
+        //m_gkbdlayout.group_changed.connect((object) => {});
+
+        m_changed_layout_timer = new GLib.Timer();
+        m_changed_layout_timer.start();
+        m_gkbdlayout.start_listen();
+    }
+
+    private void init_engines_order() {
+        if (m_config == null) {
+            return;
+        }
+
+        m_xkblayout = new XKBLayout(m_config);
+
+        if (HAVE_IBUS_GKBD) {
+            init_gkbd();
+        }
+
+        update_xkb_engines();
+    }
+
+    private void update_xkb_engines() {
+        string var_layout = m_xkblayout.get_layout();
+        string var_variant = m_xkblayout.get_variant();
+        if (var_layout == "") {
+            return;
+        }
+
+        m_layouts = var_layout.split(",");
+        m_variants = var_variant.split(",");
+
+        IBus.XKBConfigRegistry registry = new IBus.XKBConfigRegistry();
+        string[] var_xkb_engine_names = {};
+        for (int i = 0; i < m_layouts.length; i++) {
+            string name = m_layouts[i];
+            string lang = null;
+
+            if (i < m_variants.length && m_variants[i] != "") {
+                name = "%s:%s".printf(name, m_variants[i]);
+                string layout = "%s(%s)".printf(name, m_variants[i]);
+                GLib.List<string> langs =
+                        registry.layout_lang_get_langs(layout);
+                if (langs.length() != 0) {
+                    lang = langs.data;
+                }
+            } else {
+                name = "%s:".printf(name);
+            }
+
+            if (lang == null) {
+                GLib.List<string> langs =
+                        registry.layout_lang_get_langs(m_layouts[i]);
+                if (langs.length() != 0) {
+                    lang = langs.data;
+                }
+            }
+
+            var_xkb_engine_names += "%s:%s:%s".printf("xkb", name, lang);
+        }
+
+        GLib.Variant var_engines = 
+           m_config.get_value("general", "preload_engines");
+        string[] engine_names = {};
+        bool updated_engine_names = false;
+
+        if (var_engines != null) {
+            engine_names = var_engines.dup_strv();
+        }
+
+        foreach (string name in var_xkb_engine_names) {
+            if (name in engine_names)
+                continue;
+            updated_engine_names = true;
+            engine_names += name;
+        }
+
+        if (updated_engine_names) {
+            m_config.set_value("general",
+                               "preload_engines",
+                               new GLib.Variant.strv(engine_names));
+        }
+
+        GLib.Variant var_order =
+            m_config.get_value("general", "engines_order");
+        string[] order_names = {};
+        bool updated_order_names = false;
+
+        if (var_order != null) {
+            order_names = var_order.dup_strv();
+        }
+
+        foreach (var name in var_xkb_engine_names) {
+            if (name in order_names)
+                continue;
+            order_names += name;
+            updated_order_names = true;
+        }
+
+        if (updated_order_names) {
+            m_config.set_value("general",
+                               "engines_order",
+                               new GLib.Variant.strv(order_names));
+        }
+    }
+
+    private void set_xkb_group_layout(string layout) {
+        int[] retval = m_xkblayout.set_layout(layout);
+        if (retval[0] >= 0) {
+            /* If an XKB keymap is added into the XKB group,
+             * this._gkbdlayout.lock_group will be called after
+             * 'group-changed' signal is received. */
+            m_fallback_lock_id = retval[0];
+            m_changed_xkb_option = (retval[1] != 0) ? true : false;
+        }
+    }
+
+    private bool set_gkbd_layout(string layout) {
+        /* If a previous ibus engine changed XKB options, need to set the
+         * default XKB option. */
+        if (m_changed_xkb_option == true) {
+            m_changed_xkb_option = false;
+            return false;
+        }
+
+        int gkbd_len = m_gkbdlayout.get_group_names().length;
+        for (int i = 0; i < m_layouts.length && i < gkbd_len; i++) {
+            string sys_layout = m_layouts[i];
+            if (i < m_variants.length && m_variants[i] != "") {
+                sys_layout = "%s(%s)".printf(sys_layout, m_variants[i]);
+            }
+            if (sys_layout == layout) {
+                m_gkbdlayout.lock_group(i);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void set_layout(string layout) {
+        if (layout == "default" || layout == null) {
+            return;
+        }
+
+        if (m_xkblayout == null) {
+            init_engines_order();
+        }
+
+        if (HAVE_IBUS_GKBD) {
+            if (set_gkbd_layout(layout)) {
+                return;
+            }
+            set_xkb_group_layout(layout);
+            return;
+        }
+
+        m_xkblayout.set_layout(layout);
+    }
+
     private void switch_engine(int i, bool force = false) {
         GLib.assert(i >= 0 && i < m_engines.length);
 
@@ -121,15 +368,7 @@ class Panel : IBus.PanelService {
             return;
         }
         // set xkb layout
-        string cmdline = "setxkbmap %s".printf(engine.get_layout());
-        try {
-            if (!GLib.Process.spawn_command_line_sync(cmdline)) {
-                warning("Switch xkb layout to %s failed.",
-                    engine.get_layout());
-            }
-        } catch (GLib.SpawnError e) {
-            warning("execute setxkblayout failed");
-        }
+        set_layout(engine.get_layout());
 
         string[] names = {};
         foreach(var desc in m_engines) {
diff --git a/ui/gtk3/xkblayout.vala b/ui/gtk3/xkblayout.vala
new file mode 100644
index 0000000..8b75387
--- /dev/null
+++ b/ui/gtk3/xkblayout.vala
@@ -0,0 +1,466 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright(c) 2012 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2012 Takao Fujiwara <tfujiwar@redhat.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
+ */
+
+using IBus;
+
+public extern const bool HAVE_IBUS_GKBD;
+public extern const bool HAVE_IBUS_XKB;
+public extern const int XKB_LAYOUTS_MAX_LENGTH;
+public extern const string IBUS_XKB_COMMAND;
+
+class XKBLayout
+{
+    string m_xkb_command = IBUS_XKB_COMMAND;
+    IBus.Config m_config = null;
+    string[] m_xkb_latin_layouts = {};
+    GLib.Pid m_xkb_pid = -1;
+    GLib.Pid m_xmodmap_pid = -1;
+    string m_xmodmap_command = "xmodmap";
+    bool m_use_xmodmap = true;
+    string[] m_xmodmap_known_files = {".xmodmap", ".xmodmaprc",
+                                      ".Xmodmap", ".Xmodmaprc"};
+    string m_default_layout = "";
+    string m_default_variant = "";
+    string m_default_option = "";
+
+    public XKBLayout(IBus.Config? config) {
+        if (!HAVE_IBUS_XKB) {
+            m_xkb_command = "setxkbmap";
+        }
+
+        m_config = config;
+
+        if (config != null) {
+            var value  = config.get_value("general", "xkb_latin_layouts");
+            for (int i = 0; value != null && i < value.n_children(); i++) {
+                m_xkb_latin_layouts +=
+                        value.get_child_value(i).dup_string();
+            }
+            if (m_use_xmodmap) {
+                m_use_xmodmap = config.get_value("general", "use_xmodmap").get_boolean();
+            }
+        }
+    }
+
+    private string get_output_from_cmdline(string arg, string element) {
+        string[] exec_command = {};
+        exec_command += m_xkb_command;
+        exec_command += arg;
+        string standard_output = null;
+        string standard_error = null;
+        int exit_status = 0;
+        string retval = "";
+        try {
+            GLib.Process.spawn_sync(null,
+                                    exec_command,
+                                    null,
+                                    GLib.SpawnFlags.SEARCH_PATH,
+                                    null,
+                                    out standard_output,
+                                    out standard_error,
+                                    out exit_status);
+        } catch (GLib.SpawnError err) {
+            stderr.printf("IBUS_ERROR: %s\n", err.message);
+        }
+        if (exit_status != 0) {
+            stderr.printf("IBUS_ERROR: %s\n", standard_error ?? "");
+        }
+        if (standard_output == null) {
+            return "";
+        }
+        foreach (string line in standard_output.split("\n")) {
+            if (element.length <= line.length &&
+                line[0:element.length] == element) {
+                retval = line[element.length:line.length];
+            }
+        }
+        return retval;
+    }
+
+    private void set_layout_cb(GLib.Pid pid, int status) {
+        if (m_xkb_pid != pid) {
+            stderr.printf("IBUS_ERROR: set_layout_cb has another pid\n");
+            return;
+        }
+        GLib.Process.close_pid(m_xkb_pid);
+        m_xkb_pid = -1;
+        set_xmodmap();
+    }
+
+    private void set_xmodmap_cb(GLib.Pid pid, int status) {
+        if (m_xmodmap_pid != pid) {
+            stderr.printf("IBUS_ERROR: set_xmodmap_cb has another pid\n");
+            return;
+        }
+        GLib.Process.close_pid(m_xmodmap_pid);
+        m_xmodmap_pid = -1;
+    }
+
+    private string get_fullpath(string command) {
+        string envpath = GLib.Environment.get_variable("PATH");
+        foreach (string dir in envpath.split(":")) {
+            string filepath = GLib.Path.build_filename(dir, command);
+            if (GLib.FileUtils.test(filepath, GLib.FileTest.EXISTS)) {
+                return filepath;
+            }
+        }
+        return "";
+    }
+
+    private string[] get_xkb_group_layout (string layout,
+                                           string variant,
+                                           int layouts_max_length) {
+        int group_id = 0;
+        int i = 0;
+        string[] layouts = m_default_layout.split(",");
+        string[] variants = m_default_variant.split(",");
+        string group_layouts = "";
+        string group_variants = "";
+        bool has_variant = false;
+        bool include_keymap = false;
+
+        for (i = 0; i < layouts.length; i++) {
+            if (i >= layouts_max_length - 1) {
+                break;
+            }
+
+            if (i == 0) {
+                group_layouts = layouts[i];
+            } else {
+                group_layouts = "%s,%s".printf(group_layouts, layouts[i]);
+            }
+
+            if (i >= variants.length) {
+                if (i == 0) {
+                    group_variants = "";
+                } else {
+                    group_variants += ",";
+                }
+                if (layout == layouts[i] && variant == "") {
+                    include_keymap = true;
+                    group_id = i;
+                }
+                continue;
+            }
+            if (layout == layouts[i] && variant == variants[i]) {
+                include_keymap = true;
+                group_id = i;
+            }
+
+            if (variants[i] != "") {
+                has_variant = true;
+            }
+
+            if (i == 0) {
+                group_variants = variants[i];
+            } else {
+                group_variants = "%s,%s".printf(group_variants, variants[i]);
+            }
+        }
+
+        if (variant != "") {
+            has_variant = true;
+        }
+
+        if (!include_keymap) {
+            group_layouts = "%s,%s".printf(group_layouts, layout);
+            group_variants = "%s,%s".printf(group_variants, variant);
+            group_id = i;
+        }
+
+        if (!has_variant) {
+            group_variants = null;
+        }
+
+        return {group_layouts, group_variants, group_id.to_string()};
+    }
+
+    public string[] get_variant_from_layout(string layout) {
+        int left_bracket = layout.index_of("(");
+        int right_bracket = layout.index_of(")");
+        if (left_bracket >= 0 && right_bracket > left_bracket) {
+            return {layout[0:left_bracket] +
+                    layout[right_bracket + 1:layout.length],
+                    layout[left_bracket + 1:right_bracket]};
+        }
+        return {layout, "default"};
+    }
+
+    public string[] get_option_from_layout(string layout) {
+        int left_bracket = layout.index_of("[");
+        int right_bracket = layout.index_of("]");
+        if (left_bracket >= 0 && right_bracket > left_bracket) {
+            return {layout[0:left_bracket] +
+                    layout[right_bracket + 1:layout.length],
+                    layout[left_bracket + 1:right_bracket]};
+        }
+        return {layout, "default"};
+    }
+
+    public string get_layout() {
+        if (HAVE_IBUS_XKB) {
+            return get_output_from_cmdline("--get", "layout: ");
+        } else {
+            return get_output_from_cmdline("-query", "layout: ");
+        }
+    }
+
+    public string get_variant() {
+        if (HAVE_IBUS_XKB) {
+            return get_output_from_cmdline("--get", "variant: ");
+        } else {
+            return get_output_from_cmdline("-query", "variant: ");
+        }
+    }
+
+    public string get_option() {
+        if (HAVE_IBUS_XKB) {
+            return get_output_from_cmdline("--get", "option: ");
+        } else {
+            return get_output_from_cmdline("-query", "options: ");
+        }
+    }
+
+    /*
+    public string get_group() {
+        return get_output_from_cmdline("--get-group", "group: ");
+    }
+    */
+
+    public int[] set_layout(string _layout="default",
+                            string _variant="default",
+                            string _option="default") {
+        assert (_layout != null);
+
+        int xkb_group_id = 0;
+        int changed_option = 0;
+
+        if (m_xkb_pid != -1) {
+            return {-1, 0};
+        }
+
+        if (_layout == "default" && _variant == "default" &&
+            _option == "default") {
+            return {-1, 0};
+        }
+        // const gchar to gchar
+        string layout = _layout;
+        string variant = _variant;
+        string option = _option;
+
+        if (variant == "default") {
+            string[] array = get_variant_from_layout(layout);
+            layout = array[0];
+            variant = array[1];
+        }
+
+        if (option == "default") {
+            string[] array = get_option_from_layout(layout);
+            layout = array[0];
+            option = array[1];
+        }
+
+        bool need_us_layout = false;
+        foreach (string latin_layout in m_xkb_latin_layouts) {
+            if (layout == latin_layout && variant != "eng") {
+                need_us_layout = true;
+                break;
+            }
+            if (variant != null &&
+                "%s(%s)".printf(layout, variant) == latin_layout) {
+                need_us_layout = true;
+                break;
+            }
+        }
+
+        int layouts_max_length =  XKB_LAYOUTS_MAX_LENGTH;
+        if (need_us_layout) {
+            layouts_max_length--;
+        }
+
+        if (m_default_layout == "") {
+            m_default_layout = get_layout();
+        }
+        if (m_default_variant  == "") {
+            m_default_variant  = get_variant();
+        }
+        if (m_default_option == "") {
+            m_default_option = get_option();
+        }
+
+        if (layout == "default") {
+            layout = m_default_layout;
+            variant = m_default_variant;
+        } else {
+            if (HAVE_IBUS_GKBD) {
+                if (variant == "default") {
+                    variant = "";
+                }
+                string[] retval = get_xkb_group_layout (layout, variant,
+                                                        layouts_max_length);
+                layout = retval[0];
+                variant = retval[1];
+                xkb_group_id = int.parse(retval[2]);
+            }
+        }
+
+        if (layout == "") {
+            warning("Could not get the correct layout");
+            return {-1, 0};
+        }
+
+        if (variant == "default" || variant == "") {
+            variant = null;
+        }
+
+        if (option == "default" || option == "") {
+            option = m_default_option;
+        } else {
+            if (!(option in m_default_option.split(","))) {
+                option = "%s,%s".printf(m_default_option, option);
+                changed_option = 1;
+            } else {
+                option = m_default_option;
+            }
+        }
+
+        if (option == "") {
+            option = null;
+        }
+
+        if (need_us_layout) {
+            layout += ",us";
+            if (variant != null) {
+                variant += ",";
+            }
+        }
+
+        string[] args = {};
+        args += m_xkb_command;
+        if (HAVE_IBUS_XKB) {
+            args += "--layout";
+            args += layout;
+            if (variant != null) {
+                args += "--variant";
+                args += variant;
+            }
+            if (option != null) {
+                args += "--option";
+                args += option;
+            }
+        } else {
+            args += layout;
+            if (variant != null) {
+                args += variant;
+            }
+            if (option != null) {
+                args += option;
+            }
+        }
+
+        GLib.Pid child_pid;
+        try {
+            GLib.Process.spawn_async(null,
+                                     args,
+                                     null,
+                                     GLib.SpawnFlags.DO_NOT_REAP_CHILD |
+                                         GLib.SpawnFlags.SEARCH_PATH,
+                                     null,
+                                     out child_pid);
+        } catch (GLib.SpawnError err) {
+            stderr.printf("IBUS_ERROR: %s\n", err.message);
+            return {-1, 0};
+        }
+        m_xkb_pid = child_pid;
+        GLib.ChildWatch.add(m_xkb_pid, set_layout_cb);
+
+        return {xkb_group_id, changed_option};
+    }
+
+    public void set_xmodmap() {
+        if (!m_use_xmodmap) {
+            return;
+        }
+
+        if (m_xmodmap_pid != -1) {
+            return;
+        }
+
+        string xmodmap_cmdpath = get_fullpath(m_xmodmap_command);
+        if (xmodmap_cmdpath == "") {
+            xmodmap_cmdpath = m_xmodmap_command;
+        }
+        string homedir = GLib.Environment.get_home_dir();
+        foreach (string xmodmap_file in m_xmodmap_known_files) {
+            string xmodmap_filepath = GLib.Path.build_filename(homedir, xmodmap_file);
+            if (!GLib.FileUtils.test(xmodmap_filepath, GLib.FileTest.EXISTS)) {
+                continue;
+            }
+            string[] args = {xmodmap_cmdpath, xmodmap_filepath};
+
+            GLib.Pid child_pid;
+            try {
+                GLib.Process.spawn_async(null,
+                                         args,
+                                         null,
+                                         GLib.SpawnFlags.DO_NOT_REAP_CHILD |
+                                             GLib.SpawnFlags.SEARCH_PATH,
+                                         null,
+                                         out child_pid);
+            } catch (GLib.SpawnError err) {
+                stderr.printf("IBUS_ERROR: %s\n", err.message);
+                return;
+            }
+            m_xmodmap_pid = child_pid;
+            GLib.ChildWatch.add(m_xmodmap_pid, set_xmodmap_cb);
+
+            break;
+        }
+    }
+
+    public void reset_layout() {
+        m_default_layout = get_layout();
+        m_default_variant = get_variant();
+        m_default_option = get_option();
+    }
+
+    /*
+    public static int main(string[] args) {
+        IBus.Bus bus = new IBus.Bus();
+        IBus.Config config = bus.get_config();
+        XKBLayout xkblayout = new XKBLayout(config);
+        stdout.printf ("layout: %s\n", xkblayout.get_layout());
+        stdout.printf ("variant: %s\n", xkblayout.get_variant());
+        stdout.printf ("option: %s\n", xkblayout.get_option());
+        xkblayout.set_layout("jp");
+        if (config != null) {
+            IBus.main();
+        } else {
+            Gtk.init (ref args);
+            Gtk.main();
+        }
+        return 0;
+    }
+    */
+}
-- 
1.7.9.1