[caribou/wip/xkbfile] Load keyboard symbols from xkbfile as a fallback



commit 6865cb03ff476b5c4cf18ff0bb505e87fa8ef7f9
Author: Daiki Ueno <ueno unixuser org>
Date:   Tue Jan 8 15:39:49 2013 +0900

    Load keyboard symbols from xkbfile as a fallback
    
    When not layout file is found, try to read symbols from XKB rules
    and replace symbols in the base ("us") layout.

 configure.ac                     |    4 +
 libcaribou/Makefile.am           |    5 +-
 libcaribou/config.vapi           |    6 ++
 libcaribou/external-libs.vapi    |   46 ++++++++++++++
 libcaribou/key-model.vala        |   28 ++++++---
 libcaribou/keyboard-model.vala   |    5 ++
 libcaribou/xadapter.vala         |  120 ++++++++++++++++++++++++++++++++++++++
 libcaribou/xml-deserializer.vala |    7 --
 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);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]