diff options
author | Daiki Ueno <ueno@unixuser.org> | 2013-01-08 15:39:49 +0900 |
---|---|---|
committer | Daiki Ueno <ueno@unixuser.org> | 2013-01-08 18:12:00 +0900 |
commit | 6865cb03ff476b5c4cf18ff0bb505e87fa8ef7f9 (patch) | |
tree | acd513178974f950c2e7be937d8c167776c9cc86 | |
parent | 8532649f780515b85077c596c1159be76126272e (diff) | |
download | caribou-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.ac | 4 | ||||
-rw-r--r-- | libcaribou/Makefile.am | 5 | ||||
-rw-r--r-- | libcaribou/config.vapi | 6 | ||||
-rw-r--r-- | libcaribou/external-libs.vapi | 46 | ||||
-rw-r--r-- | libcaribou/key-model.vala | 28 | ||||
-rw-r--r-- | libcaribou/keyboard-model.vala | 5 | ||||
-rw-r--r-- | libcaribou/xadapter.vala | 120 | ||||
-rw-r--r-- | 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); |