From 8085ca38cb9c2ea370ebe64d80a4d7894a485960 Mon Sep 17 00:00:00 2001
From: fujiwarat <takao.fujiwara1@gmail.com>
Date: Tue, 14 Jan 2014 15:27:50 +0900
Subject: [PATCH] Add libgnomekbd and load preload engines.
---
bindings/vala/Gkbd-3.0.metadata | 1 +
bindings/vala/Makefile.am | 19 +-
bindings/vala/Xkl-1.0.metadata | 3 +
bindings/vala/gkbd.deps | 3 +
bus/ibusimpl.c | 12 +-
configure.ac | 40 ++++
data/ibus.schemas.in | 59 +++++
ibus-1.0.pc.in | 1 +
ibus.spec.in | 12 ++
src/Makefile.am | 3 +
src/ibus.h | 1 +
src/ibusxkbxml.c | 466 ++++++++++++++++++++++++++++++++++++++++
src/ibusxkbxml.h | 187 ++++++++++++++++
ui/gtk3/Makefile.am | 36 ++++
ui/gtk3/gkbdlayout.vala.false | 63 ++++++
ui/gtk3/gkbdlayout.vala.true | 108 ++++++++++
ui/gtk3/panel.vala | 196 ++++++++++++++++-
ui/gtk3/xkblayout.vala | 429 ++++++++++++++++++++++++++++++++++++
18 files changed, 1634 insertions(+), 5 deletions(-)
create mode 100644 bindings/vala/Gkbd-3.0.metadata
create mode 100644 bindings/vala/Xkl-1.0.metadata
create mode 100644 bindings/vala/gkbd.deps
create mode 100644 src/ibusxkbxml.c
create mode 100644 src/ibusxkbxml.h
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/bindings/vala/Gkbd-3.0.metadata b/bindings/vala/Gkbd-3.0.metadata
new file mode 100644
index 0000000..661e6fd
--- /dev/null
+++ b/bindings/vala/Gkbd-3.0.metadata
@@ -0,0 +1 @@
+Configuration cheader_filename="libgnomekbd/gkbd-configuration.h"
diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
index 29cc1eb..1d28501 100644
--- a/bindings/vala/Makefile.am
+++ b/bindings/vala/Makefile.am
@@ -28,8 +28,6 @@ vapi_deps = \
$(top_builddir)/src/IBus-1.0.gir \
$(NULL)
-ibus-1.0.vapi: $(vapi_deps)
-
VAPIGEN_VAPIS = ibus-1.0.vapi
ibus_1_0_vapi_DEPS = gio-2.0
@@ -39,19 +37,34 @@ ibus_1_0_vapi_FILES = \
$(srcdir)/IBus-1.0-custom.vala \
$(NULL)
+if ENABLE_LIBGNOMEKBD
+ibus-1.0.vapi: $(vapi_deps) gkbd.vapi
+
+VAPIGEN_VAPIS += gkbd.vapi
+
+gkbd_vapi_DEPS = gtk+-3.0 glib-2.0 gmodule-2.0
+gkbd_vapi_METADATADIRS = $(srcdir)
+gkbd_vapi_FILES = /usr/share/gir-1.0/Gkbd-3.0.gir
+else
+ibus-1.0.vapi: $(vapi_deps)
+endif
+
vapidir = $(datadir)/vala/vapi
-vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
+vapi_DATA = ibus-1.0.vapi ibus-1.0.deps
MAINTAINERCLEANFILES = $(VAPIGEN_VAPIS)
EXTRA_DIST = \
$(VAPIGEN_VAPIS) \
+ Gkbd-3.0.metadata \
+ gkbd.deps \
IBus-1.0.metadata \
IBus-1.0-custom.vala \
ibus-1.0.deps \
ibus-private.vapi \
config.vapi \
xi.vapi \
+ Xkl-1.0.metadata \
$(NULL)
-include $(top_srcdir)/git.mk
diff --git a/bindings/vala/Xkl-1.0.metadata b/bindings/vala/Xkl-1.0.metadata
new file mode 100644
index 0000000..4961d0c
--- /dev/null
+++ b/bindings/vala/Xkl-1.0.metadata
@@ -0,0 +1,3 @@
+Xkl cheader_filename="libxklavier/xklavier.h"
+Engine
+ .filter_events.evt ref type="X.Event"
diff --git a/bindings/vala/gkbd.deps b/bindings/vala/gkbd.deps
new file mode 100644
index 0000000..172632c
--- /dev/null
+++ b/bindings/vala/gkbd.deps
@@ -0,0 +1,3 @@
+gtk+-3.0
+glib-2.0
+gmodule-2.0
diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
index eec6da3..f84c034 100644
--- a/bus/ibusimpl.c
+++ b/bus/ibusimpl.c
@@ -1135,7 +1135,17 @@ _ibus_get_engines_by_names (BusIBusImpl *ibus,
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
while (names[i] != NULL) {
IBusEngineDesc *desc = (IBusEngineDesc *) g_hash_table_lookup (
- ibus->engine_table, names[i++]);
+ ibus->engine_table, names[i]);
+
+ /* preload engines return user XKB so if the engine does not
+ * exist in simple.xml, fall back to 'us' layout. */
+ if (desc == NULL && g_str_has_prefix (names[i], "xkb:")) {
+ desc = (IBusEngineDesc *) g_hash_table_lookup (
+ ibus->engine_table, "xkb:us::eng");
+ }
+
+ i++;
+
if (desc == NULL)
continue;
g_variant_builder_add (
diff --git a/configure.ac b/configure.ac
index d71c415..779e29e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -242,6 +242,45 @@ else
enable_xim="no (disabled, use --enable-xim to enable)"
fi
+# Option for XKB command.
+PKG_CHECK_MODULES(XKB,
+ [xkbfile],,
+ [XKB_LIBS="-lxkbfile"]
+)
+
+# --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])
@@ -595,6 +634,7 @@ Build options:
Panel icon "$IBUS_ICON_KEYBOARD"
Enable surrounding-text $enable_surrounding_text
Enable libnotify $enable_libnotify
+ Build libgnomebkd $enable_libgnomekbd
Run test cases $enable_tests
])
diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
index d256cbb..8fd7e15 100644
--- a/data/ibus.schemas.in
+++ b/data/ibus.schemas.in
@@ -56,6 +56,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/general/hotkey/trigger</key>
<applyto>/desktop/ibus/general/hotkey/trigger</applyto>
<owner>ibus</owner>
@@ -80,6 +126,19 @@
</locale>
</schema>
<schema>
+ <key>/schemas/desktop/ibus/general/hotkey/triggers-no-modifiers</key>
+ <applyto>/desktop/ibus/general/hotkey/triggers-no-modifiers</applyto>
+ <owner>ibus</owner>
+ <type>list</type>
+ <list_type>string</list_type>
+ <default>[]</default>
+ <locale name="C">
+ <short>Trigger shortcut keys without modifier keys</short>
+ <long>Trigger shortcut keys without modifier keys.
+ The list is used by ibus-gjs.</long>
+ </locale>
+ </schema>
+ <schema>
<key>/schemas/desktop/ibus/general/hotkey/enable_unconditional</key>
<applyto>/desktop/ibus/general/hotkey/enable_unconditional</applyto>
<owner>ibus</owner>
diff --git a/ibus-1.0.pc.in b/ibus-1.0.pc.in
index 9f593ab..c93a0ed 100644
--- a/ibus-1.0.pc.in
+++ b/ibus-1.0.pc.in
@@ -4,6 +4,7 @@ libdir=@libdir@
includedir=@includedir@
datadir=@datadir@
pkgdatadir=@datadir@/ibus
+have_ibus_gkbd=@HAVE_IBUS_GKBD@
Name: IBus
Description: IBus Library
diff --git a/ibus.spec.in b/ibus.spec.in
index 334f37e..2017af9 100644
--- a/ibus.spec.in
+++ b/ibus.spec.in
@@ -5,6 +5,7 @@
# Build flags
%define build_python_library 0
+%define build_libgnomekbd 0
%define glib_ver %([ -a %{_libdir}/pkgconfig/glib-2.0.pc ] && pkg-config --modversion glib-2.0 | cut -d. -f 1,2 || echo -n "999")
%define gconf2_version 2.12.0
@@ -40,6 +41,10 @@ BuildRequires: dconf-devel
BuildRequires: pygobject2-devel
BuildRequires: intltool
BuildRequires: iso-codes-devel
+%if %{build_libgnomekbd}
+BuildRequires: libxkbfile-devel
+BuildRequires: libgnomekbd-devel
+%endif
Requires: %{name}-libs = %{version}-%{release}
Requires: %{name}-gtk2 = %{version}-%{release}
@@ -52,6 +57,9 @@ Requires: dbus-python >= %{dbus_python_version}
Requires: im-chooser >= %{im_chooser_version}
Requires: notify-python
Requires: librsvg2
+%if %{build_libgnomekbd}
+Requires: libgnomekbd
+%endif
Requires(post): desktop-file-utils
Requires(postun): desktop-file-utils
@@ -152,6 +160,10 @@ OPTIONS="$OPTIONS --enable-python-library"
OPTIONS="$OPTIONS --disable-python-library"
%endif
+%if %{build_libgnomekbd}
+OPTIONS="$OPTIONS --enable-libgnomekbd"
+%endif
+
%configure $OPTIONS
# make -C po update-gmo
diff --git a/src/Makefile.am b/src/Makefile.am
index 404e1d2..f00fab7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -202,6 +202,9 @@ typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
CLEANFILES += $(dist_gir_DATA) $(typelibs_DATA)
endif
+ibus_sources += ibusxkbxml.c
+ibus_headers += ibusxkbxml.h
+
# 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 d8e226e..f0a9456 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..f815e5d
--- /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) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2013 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..5aa486d
--- /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) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2013 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/Makefile.am b/ui/gtk3/Makefile.am
index 6012cfa..3dfa0df 100644
--- a/ui/gtk3/Makefile.am
+++ b/ui/gtk3/Makefile.am
@@ -44,6 +44,8 @@ AM_CPPFLAGS = \
USE_SYMBOL_ICON = FALSE
+HAVE_IBUS_GKBD_C = $(strip $(subst false, FALSE, $(subst true, TRUE, $(HAVE_IBUS_GKBD))))
+
AM_CFLAGS = \
@GLIB2_CFLAGS@ \
@GIO2_CFLAGS@ \
@@ -54,6 +56,8 @@ AM_CFLAGS = \
-DBINDIR=\"$(bindir)\" \
-DIBUS_DISABLE_DEPRECATED \
-DSWITCHER_USE_SYMBOL_ICON=$(USE_SYMBOL_ICON) \
+ -DHAVE_IBUS_GKBD=$(HAVE_IBUS_GKBD_C) \
+ -DXKB_LAYOUTS_MAX_LENGTH=4 \
-Wno-unused-variable \
-Wno-unused-but-set-variable \
-Wno-unused-function \
@@ -97,12 +101,40 @@ AM_VALAFLAGS += \
$(NULL)
endif
+if ENABLE_LIBGNOMEKBD
+AM_CFLAGS += \
+ @LIBGNOMEKBDUI_CFLAGS@ \
+ @ATK_CFLAGS@ \
+ $(NULL)
+
+AM_LDADD += \
+ @LIBGNOMEKBDUI_LIBS@ \
+ @ATK_LIBS@ \
+ $(NULL)
+
+AM_VALAFLAGS += \
+ --vapidir=. \
+ --metadatadir=$(top_srcdir)/bindings/vala \
+ --pkg=glib-2.0 \
+ --pkg=gmodule-2.0 \
+ --pkg=gkbd \
+ --pkg=Xkl-1.0 \
+ $(NULL)
+
+$(srcdir)/gkbdlayout.vala: $(top_builddir)/bindings/vala/gkbd.vapi
+ @cp $(srcdir)/gkbdlayout.vala.true $(srcdir)/gkbdlayout.vala
+else
+$(srcdir)/gkbdlayout.vala:
+ @cp $(srcdir)/gkbdlayout.vala.false $(srcdir)/gkbdlayout.vala
+endif
+
libexec_PROGRAMS = ibus-ui-gtk3
ibus_ui_gtk3_SOURCES = \
application.vala \
candidatearea.vala \
candidatepanel.vala \
+ gkbdlayout.vala \
handle.vala \
iconwidget.vala \
keybindingmanager.vala \
@@ -111,6 +143,7 @@ ibus_ui_gtk3_SOURCES = \
property.vala \
separator.vala \
switcher.vala \
+ xkblayout.vala \
$(NULL)
ibus_ui_gtk3_LDADD = \
@@ -119,9 +152,12 @@ ibus_ui_gtk3_LDADD = \
CLEANFILES = \
gtkpanel.xml \
+ gkbdlayout.vala \
$(NULL)
EXTRA_DIST = \
+ gkbdlayout.vala.false \
+ gkbdlayout.vala.true \
gtkpanel.xml.in \
$(NULL)
diff --git a/ui/gtk3/gkbdlayout.vala.false b/ui/gtk3/gkbdlayout.vala.false
new file mode 100644
index 0000000..506aff2
--- /dev/null
+++ b/ui/gtk3/gkbdlayout.vala.false
@@ -0,0 +1,63 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright(c) 2013 Red Hat, Inc.
+ * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2013 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..a6e0f8d
--- /dev/null
+++ b/ui/gtk3/gkbdlayout.vala.true
@@ -0,0 +1,108 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright(c) 2013 Red Hat, Inc.
+ * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2013 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);
+
+ 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 a7a0c40..1da7966 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -58,6 +58,13 @@ class Panel : IBus.PanelService {
private Gtk.CssProvider m_css_provider;
private int m_switcher_delay_time = 400;
private bool m_use_system_keyboard_layout = false;
+ 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 GLib.List<Keybinding> m_keybindings = new GLib.List<Keybinding>();
@@ -108,6 +115,14 @@ class Panel : IBus.PanelService {
~Panel() {
unbind_switch_shortcut();
+
+ if (HAVE_IBUS_GKBD && m_gkbdlayout != null) {
+ m_gkbdlayout.changed.disconnect(gkbdlayout_changed_cb);
+ m_gkbdlayout.stop_listen();
+ m_gkbdlayout = null;
+ }
+
+ m_xkblayout = null;
}
private void init_settings() {
@@ -444,6 +459,7 @@ class Panel : IBus.PanelService {
}
public void load_settings() {
+ init_engines_order();
// Update m_use_system_keyboard_layout before update_engines()
// is called.
set_use_system_keyboard_layout();
@@ -463,6 +479,184 @@ class Panel : IBus.PanelService {
set_version();
}
+ 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() {
+ m_xkblayout = new XKBLayout();
+ string session = Environment.get_variable("DESKTOP_SESSION");
+
+ if (HAVE_IBUS_GKBD &&
+ session != null && session.length >= 5 &&
+ session[0:5] == "gnome") {
+ 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);
+ }
+
+ string[] engine_names =
+ m_settings_general.get_strv("preload-engines");
+ bool updated_engine_names = false;
+
+ 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_settings_general.set_strv("preload-engines", engine_names);
+
+ string[] order_names =
+ m_settings_general.get_strv("engines-order");
+ bool updated_order_names = false;
+
+ 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_settings_general.set_strv("engines-order", order_names);
+ }
+
+ private void set_xkb_group_layout(IBus.EngineDesc engine) {
+ int[] retval = m_xkblayout.set_layout(engine, true);
+ 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(IBus.EngineDesc engine) {
+ string layout = engine.get_layout();
+ string variant = engine.get_layout_variant();
+
+ /* 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;
+ }
+
+ if (variant != "" && variant != "default") {
+ layout = "%s(%s)".printf(layout, variant);
+ }
+
+ 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(IBus.EngineDesc engine) {
+ string layout = engine.get_layout();
+
+ if (layout == "" || layout == null) {
+ return;
+ }
+
+ if (m_xkblayout == null) {
+ init_engines_order();
+ }
+
+ if (HAVE_IBUS_GKBD && m_gkbdlayout != null) {
+ if (set_gkbd_layout(engine)) {
+ return;
+ }
+ set_xkb_group_layout(engine);
+ return;
+ }
+
+ m_xkblayout.set_layout(engine);
+ }
+
private void exec_setxkbmap(IBus.EngineDesc engine) {
string layout = engine.get_layout();
string variant = engine.get_layout_variant();
@@ -528,7 +722,7 @@ class Panel : IBus.PanelService {
// set xkb layout
if (!m_use_system_keyboard_layout)
- exec_setxkbmap(engine);
+ set_layout(engine);
engine_contexts_insert(engine);
}
diff --git a/ui/gtk3/xkblayout.vala b/ui/gtk3/xkblayout.vala
new file mode 100644
index 0000000..b7dfb3e
--- /dev/null
+++ b/ui/gtk3/xkblayout.vala
@@ -0,0 +1,429 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright(c) 2013 Red Hat, Inc.
+ * Copyright(c) 2013 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2013 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 extern const bool HAVE_IBUS_GKBD;
+public extern const int XKB_LAYOUTS_MAX_LENGTH;
+
+class XKBLayout
+{
+ string m_xkb_command = "setxkbmap";
+ GLib.Settings m_settings_general;
+ 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() {
+ m_settings_general = new GLib.Settings("org.freedesktop.ibus.general");
+
+ var value = m_settings_general.get_value("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 = m_settings_general.get_boolean("use-xmodmap");
+ }
+ }
+
+ 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];
+ if (retval == null) {
+ retval = "";
+ } else {
+ retval = retval.strip();
+ }
+ }
+ }
+ 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() {
+ return get_output_from_cmdline("-query", "layout: ");
+ }
+
+ public string get_variant() {
+ return get_output_from_cmdline("-query", "variant: ");
+ }
+
+ public string get_option() {
+ return get_output_from_cmdline("-query", "options: ");
+ }
+
+ /*
+ public string get_group() {
+ return get_output_from_cmdline("--get-group", "group: ");
+ }
+ */
+
+ public int[] set_layout(IBus.EngineDesc engine,
+ bool use_group_layout=false) {
+ string layout = engine.get_layout();
+ string variant = engine.get_layout_variant();
+ string option = engine.get_layout_option();
+
+ 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" || variant == "") &&
+ (option == "default" || option == "")) {
+ return {-1, 0};
+ }
+
+ 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 (use_group_layout) {
+ 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;
+ args += "-layout";
+ args += layout;
+ if (variant != null) {
+ args += "-variant";
+ args += variant;
+ }
+ if (option != null) {
+ /* TODO: Need to get the session XKB options */
+ args += "-option";
+ args += "-option";
+ 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("Execute setxkbmap failed: %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.8.0