summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@unixuser.org>2013-01-08 15:39:49 +0900
committerDaiki Ueno <ueno@unixuser.org>2013-01-08 18:12:00 +0900
commit6865cb03ff476b5c4cf18ff0bb505e87fa8ef7f9 (patch)
treeacd513178974f950c2e7be937d8c167776c9cc86
parent8532649f780515b85077c596c1159be76126272e (diff)
downloadcaribou-wip/xkbfile.tar.gz
Load keyboard symbols from xkbfile as a fallbackwip/xkbfile
When not layout file is found, try to read symbols from XKB rules and replace symbols in the base ("us") layout.
-rw-r--r--configure.ac4
-rw-r--r--libcaribou/Makefile.am5
-rw-r--r--libcaribou/config.vapi6
-rw-r--r--libcaribou/external-libs.vapi46
-rw-r--r--libcaribou/key-model.vala28
-rw-r--r--libcaribou/keyboard-model.vala5
-rw-r--r--libcaribou/xadapter.vala120
-rw-r--r--libcaribou/xml-deserializer.vala7
8 files changed, 203 insertions, 18 deletions
diff --git a/configure.ac b/configure.ac
index ea4aa30..98fc8ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,6 +45,7 @@ PKG_CHECK_MODULES(LIBCARIBOU, [
gdk-3.0 >= $GDK_REQUIRED,
xtst,
x11,
+ xkbfile,
libxklavier,
libxml-2.0,
gee-0.8,
@@ -53,6 +54,9 @@ PKG_CHECK_MODULES(LIBCARIBOU, [
AC_SUBST(LIBCARIBOU_CFLAGS)
AC_SUBST(LIBCARIBOU_LIBS)
+XKB_BASE=$($PKG_CONFIG --variable xkb_base xkeyboard-config)
+AC_SUBST(XKB_BASE)
+
dnl == i18n ==
GETTEXT_PACKAGE=caribou
AC_SUBST(GETTEXT_PACKAGE)
diff --git a/libcaribou/Makefile.am b/libcaribou/Makefile.am
index fd4a117..a7c27ca 100644
--- a/libcaribou/Makefile.am
+++ b/libcaribou/Makefile.am
@@ -5,7 +5,7 @@ libcaribou_la_VALAFLAGS = \
-h caribou-internals.h \
--vapidir=. \
--pkg xtst --pkg gee-0.8 --pkg gdk-x11-3.0 --pkg libxml-2.0 \
- --pkg libxklavier --pkg external-libs \
+ --pkg libxklavier --pkg external-libs --pkg config \
--internal-vapi caribou-internals-1.0.vapi \
--library caribou-1.0 --gir _Caribou-1.0.gir \
--symbols libcaribou.symbols \
@@ -14,7 +14,8 @@ libcaribou_la_VALAFLAGS = \
libcaribou_la_CFLAGS = \
-DG_LOG_DOMAIN=\"caribou\" \
-I$(top_srcdir) \
- $(LIBCARIBOU_CFLAGS)
+ $(LIBCARIBOU_CFLAGS) \
+ -DXKB_BASE=\"$(XKB_BASE)\"
libcaribou_la_LDFLAGS = \
-export-dynamic \
diff --git a/libcaribou/config.vapi b/libcaribou/config.vapi
new file mode 100644
index 0000000..5076146
--- /dev/null
+++ b/libcaribou/config.vapi
@@ -0,0 +1,6 @@
+[CCode (cprefix = "", lower_case_cprefix = "")]
+namespace Config {
+ public const string GETTEXT_PACKAGE;
+ public const string LOCALEDIR;
+ public const string XKB_BASE;
+}
diff --git a/libcaribou/external-libs.vapi b/libcaribou/external-libs.vapi
index 1489216..2111e5c 100644
--- a/libcaribou/external-libs.vapi
+++ b/libcaribou/external-libs.vapi
@@ -6,6 +6,9 @@ namespace Xkb {
[CCode (cname = "XkbGetKeyboard")]
public Desc get_keyboard (X.Display dpy, uint which, uint device_spec);
+ [CCode (cname = "XkbGetKeyboardByName")]
+ public Desc get_keyboard_by_name (X.Display dpy, uint device_spec, Xkb.ComponentNames names, uint want, uint need, bool load);
+
[CCode (cname = "XkbSetMap")]
public void set_map (X.Display dpy, uint which, Desc xkb);
@@ -173,6 +176,16 @@ namespace Xkb {
public class Geometry {
}
+ [CCode (cname = "XkbComponentNamesRec", destroy_function = "")]
+ public struct ComponentNames {
+ string keymap;
+ string keycodes;
+ string types;
+ string compat;
+ string symbols;
+ string geometry;
+ }
+
[CCode (cname = "XkbUseCoreKbd")]
public int UseCoreKbd;
[CCode (cname = "XkbUseCorePtr")]
@@ -257,3 +270,36 @@ namespace Xkb {
[CCode (cname = "XkbKeyTypesMask")]
public int KeyTypesMask;
}
+
+[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "stdio.h,X11/XKBlib.h,X11/extensions/XKBrules.h")]
+namespace XkbRF {
+ [Compact]
+ [CCode (cname = "XkbRF_VarDefsRec", destroy_function = "")]
+ public struct VarDefs {
+ string model;
+ string layout;
+ string variant;
+ string options;
+ }
+
+ [Compact]
+ [CCode (cname = "XkbRF_RulesRec", free_function = "")]
+ public class Rules {
+ }
+
+ [CCode (cname = "XkbRF_GetNamesProp")]
+ bool get_names_prop (X.Display dpy,
+ out string rules_file,
+ ref VarDefs var_defs);
+
+ [CCode (cname = "XkbRF_Load")]
+ Rules? load (string rules_file,
+ string? locale,
+ bool want_desc,
+ bool want_rules);
+
+ [CCode (cname = "XkbRF_GetComponents")]
+ bool get_components (Rules rule,
+ VarDefs var_defs,
+ ref Xkb.ComponentNames names);
+}
diff --git a/libcaribou/key-model.vala b/libcaribou/key-model.vala
index 8709280..d09bcde 100644
--- a/libcaribou/key-model.vala
+++ b/libcaribou/key-model.vala
@@ -22,7 +22,7 @@ namespace Caribou {
public bool show_subkeys { get; private set; default = false; }
public string name { get; private set; }
- public uint keyval { get; private set; }
+ public uint keyval { get; private set; default = Gdk.Key.VoidSymbol; }
public string? text { get; private construct set; default = null; }
private uint[] _keyvals = {};
public string label { get; private set; default = ""; }
@@ -73,7 +73,9 @@ namespace Caribou {
{ "Caribou_Repeat", "\xe2\x99\xbb" }
};
- public KeyModel (string name, string? text = null) {
+ public KeyModel (string name,
+ string? text = null,
+ uint keyval = Gdk.Key.VoidSymbol) {
this.name = name;
this.text = text;
mod_mask = (Gdk.ModifierType) 0;
@@ -91,15 +93,23 @@ namespace Caribou {
int index = 0;
unichar uc;
while (text.get_next_char (ref index, out uc)) {
- uint keyval = Gdk.unicode_to_keyval (uc);
- if (keyval != uc | 0x01000000)
- _keyvals += keyval;
+ uint _keyval = Gdk.unicode_to_keyval (uc);
+ if (_keyval != uc | 0x01000000)
+ _keyvals += _keyval;
}
- } else {
- uint keyval = Gdk.keyval_from_name (name);
- if (keyval != Gdk.Key.VoidSymbol && keyval != 0)
- _keyvals += keyval;
+ } else if (keyval != Gdk.Key.VoidSymbol) {
+ _keyvals += keyval;
this.keyval = keyval;
+ } else {
+ uint _keyval = Gdk.keyval_from_name (name);
+ // Due to GDK bug, Gdk.keyval_from_name may return
+ // 0 instead of Gdk.Key.VoidSymbol:
+ // https://bugzilla.gnome.org/show_bug.cgi?id=687024
+ if (_keyval == 0)
+ _keyval = Gdk.Key.VoidSymbol;
+ if (_keyval != Gdk.Key.VoidSymbol)
+ _keyvals += _keyval;
+ this.keyval = _keyval;
}
}
diff --git a/libcaribou/keyboard-model.vala b/libcaribou/keyboard-model.vala
index 785a668..5327026 100644
--- a/libcaribou/keyboard-model.vala
+++ b/libcaribou/keyboard-model.vala
@@ -14,6 +14,7 @@ namespace Caribou {
private Gee.HashMap<string, GroupModel> groups;
private KeyModel last_activated_key;
private Gee.HashSet<KeyModel> active_mod_keys;
+ private GroupModel base_group;
construct {
uint grpid;
@@ -25,6 +26,7 @@ namespace Caribou {
xadapter = XAdapter.get_default ();
xadapter.group_changed.connect (on_group_changed);
+ base_group = XmlDeserializer.load_group (keyboard_type, "us", "");
xadapter.get_groups (out grps, out variants);
@@ -42,6 +44,9 @@ namespace Caribou {
private void populate_group (string group, string variant) {
GroupModel grp = XmlDeserializer.load_group (keyboard_type,
group, variant);
+ if (grp == null)
+ grp = xadapter.load_group (base_group, group, variant);
+
if (grp != null) {
groups.set (GroupModel.create_group_name (group, variant), grp);
grp.key_clicked.connect (on_key_clicked);
diff --git a/libcaribou/xadapter.vala b/libcaribou/xadapter.vala
index 62ec4cc..7f9cf46 100644
--- a/libcaribou/xadapter.vala
+++ b/libcaribou/xadapter.vala
@@ -42,6 +42,7 @@ namespace Caribou {
this.xkbdesc = Xkb.get_keyboard (this.xdisplay,
Xkb.GBN_AllComponentsMask,
Xkb.UseCoreKbd);
+ this.base_desc = get_xkb_desc_for_group ("us", "");
this.xkl_engine = Xkl.Engine.get_instance (this.xdisplay);
xkl_engine.start_listen (Xkl.EngineListenModes.TRACK_KEYBOARD_STATE);
xkl_state = this.xkl_engine.get_current_state ();
@@ -347,5 +348,124 @@ namespace Caribou {
}
}
+ /* Fallback code to support multilingual keyboard layouts.
+ * When XmlDeserializer couldn't find XML file, try to read
+ * symbols from XKB files (which normally located in
+ * /usr/share/X11/xkb/rules) and replace symbols in the base
+ * (i.e. "us") layout. */
+ Xkb.Desc base_desc;
+
+ static const string XKB_MODEL = "pc105+inet";
+ static const string XKB_LAYOUT = "us";
+ static const string XKB_RULES_FILE = "evdev";
+
+ public GroupModel? load_group (GroupModel base_group,
+ string group_name,
+ string variant_name) {
+
+ var desc = get_xkb_desc_for_group (group_name, variant_name);
+ var group = new GroupModel (group_name, variant_name);
+ populate_keys_in_group (base_group, desc, group);
+ return group;
+ }
+
+ void populate_keys_in_group (GroupModel base_group,
+ Xkb.Desc desc,
+ GroupModel group)
+ {
+ var base_lnames = base_group.get_levels ();
+ foreach (var base_lname in base_lnames) {
+ var base_level = base_group.get_level (base_lname);
+ var level = new LevelModel (base_level.mode);
+ group.add_level (base_lname, level);
+
+ var base_rows = base_level.get_rows ();
+ foreach (var base_row in base_rows) {
+ var row = new RowModel ();
+ level.add_row (row);
+
+ var base_columns = base_row.get_columns ();
+ foreach (var base_column in base_columns) {
+ var column = new ColumnModel ();
+ row.add_column (column);
+
+ var base_keys = (KeyModel[])base_column.get_children ();
+ foreach (var base_key in base_keys) {
+ uint keyval = translate_keyval_in_desc (
+ desc, base_key.keyval);
+ var key = new KeyModel (base_key.name,
+ null,
+ keyval);
+ key.align = base_key.align;
+ key.toggle = base_key.toggle;
+ key.width = base_key.width;
+ column.add_key (key);
+ }
+ }
+ }
+ }
+ }
+
+ uint translate_keyval_in_desc (Xkb.Desc desc, uint base_keyval) {
+ if (base_keyval == Gdk.Key.VoidSymbol)
+ return Gdk.Key.VoidSymbol;
+ for (int i = base_desc.min_key_code; i <= base_desc.max_key_code; i++) {
+ unowned Xkb.SymMap base_symmap = base_desc.map.key_sym_map[i];
+ for (int j = 0; j < base_symmap.width; j++) {
+ if (base_desc.map.syms[base_symmap.offset + j] == base_keyval) {
+ unowned Xkb.SymMap symmap = desc.map.key_sym_map[i];
+ return (uint) desc.map.syms[symmap.offset + j];
+ }
+ }
+ }
+ return Gdk.Key.VoidSymbol;
+ }
+
+ Xkb.Desc get_xkb_desc_for_group (string group_name,
+ string variant_name)
+ {
+ XkbRF.VarDefs var_defs = XkbRF.VarDefs ();
+ string rules_file_path = get_xkb_rules_file (ref var_defs);
+
+ XkbRF.Rules? rules = XkbRF.load (rules_file_path, null, true, true);
+ if (rules == null) {
+ warning ("can't load XKB rules from %s",
+ rules_file_path);
+ return null;
+ }
+
+ var_defs.model = XKB_MODEL;
+ var_defs.layout = group_name;
+ var_defs.variant = variant_name;
+ var_defs.options = null;
+
+ Xkb.ComponentNames names = Xkb.ComponentNames ();
+ if (!XkbRF.get_components (rules, var_defs, ref names)) {
+ warning ("can't resolve XKB component names for %s %s",
+ group_name, variant_name);
+ return null;
+ }
+
+ return Xkb.get_keyboard_by_name (this.xdisplay,
+ Xkb.UseCoreKbd,
+ names,
+ 0,
+ Xkb.GBN_AllComponentsMask,
+ false);
+ }
+
+ string get_xkb_rules_file (ref XkbRF.VarDefs var_defs) {
+ string rules;
+ if (!XkbRF.get_names_prop (this.xdisplay,
+ out rules,
+ ref var_defs)) {
+ rules = XKB_RULES_FILE;
+ var_defs.model = XKB_MODEL;
+ var_defs.layout = XKB_LAYOUT;
+ var_defs.variant = null;
+ var_defs.options = null;
+ }
+ return Path.build_filename (Config.XKB_BASE, "rules", rules, null);
+ }
}
}
diff --git a/libcaribou/xml-deserializer.vala b/libcaribou/xml-deserializer.vala
index 8058c0f..616ac8b 100644
--- a/libcaribou/xml-deserializer.vala
+++ b/libcaribou/xml-deserializer.vala
@@ -41,13 +41,6 @@ namespace Caribou {
return fn;
}
- // If no layout file is found, default to US
- foreach (string data_dir in dirs) {
- string fn = get_layout_file_inner (data_dir, "us", "");
- if (fn != null)
- return fn;
- }
-
// Should not be reached, but needed to make valac happy
throw new IOError.NOT_FOUND (
"Could not find layout file for %s %s", group, variant);