diff --git a/configure.ac b/configure.ac index e9cd40c5..88822918 100644 --- a/configure.ac +++ b/configure.ac @@ -78,9 +78,13 @@ PKG_CHECK_MODULES([GEE], [gee-0.8], , PKG_CHECK_MODULES([JSON_GLIB], [json-glib-1.0], , [AC_MSG_ERROR([can't find json-glib-1.0])]) -LIBSKK_CFLAGS="$GIO_CFLAGS $GEE_CFLAGS $JSON_GLIB_CFLAGS" +# check xkbcommon +PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon], , + [AC_MSG_ERROR([can't find xkbcommon])]) + +LIBSKK_CFLAGS="$GIO_CFLAGS $GEE_CFLAGS $JSON_GLIB_CFLAGS $XKBCOMMON_CFLAGS" AC_SUBST(LIBSKK_CFLAGS) -LIBSKK_LIBS="$GIO_LIBS $GEE_LIBS $JSON_GLIB_LIBS" +LIBSKK_LIBS="$GIO_LIBS $GEE_LIBS $JSON_GLIB_LIBS $XKBCOMMON_LIBS" AC_SUBST(LIBSKK_LIBS) GOBJECT_INTROSPECTION_CHECK([0.9.0]) diff --git a/docs/Makefile.am b/docs/Makefile.am index 32daee93..8c11ba67 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -62,6 +62,7 @@ libskk_doc_deps = \ gee-0.8 \ posix \ json-glib-1.0 \ + xkbcommon \ $(NULL) valadoc_flags_libskk = \ $(valadoc_flags) \ diff --git a/libskk/Makefile.am b/libskk/Makefile.am index 1d85a8e2..79a48546 100644 --- a/libskk/Makefile.am +++ b/libskk/Makefile.am @@ -31,6 +31,7 @@ libskk_la_VALAFLAGS = \ --pkg gee-0.8 \ --pkg posix \ --pkg json-glib-1.0 \ + --pkg xkbcommon \ --internal-vapi skk-internals-@SKK_API_VERSION@.vapi \ --library skk-@SKK_API_VERSION@ \ --gir Skk-@SKK_API_VERSION@.gir \ @@ -112,6 +113,6 @@ pkgconfig_DATA = libskk.pc vapi_DATA = skk-1.0.vapi skk-1.0.deps vapidir = $(datadir)/vala/vapi -EXTRA_DIST = config.vapi skk-1.0.deps libskk.symbols +EXTRA_DIST = config.vapi skk-1.0.deps libskk.symbols xkbcommon.vapi -include $(top_srcdir)/git.mk diff --git a/libskk/key-event.vala b/libskk/key-event.vala index 5138e28f..195941aa 100644 --- a/libskk/key-event.vala +++ b/libskk/key-event.vala @@ -106,59 +106,96 @@ namespace Skk { * @return a new KeyEvent */ public KeyEvent.from_string (string key) throws KeyEventFormatError { - if (key.has_prefix ("(") && key.has_suffix (")")) { + ModifierType _modifiers = 0; + uint _keyval = Keysyms.VoidSymbol; + if (key.has_prefix ("(usleep ") && key.has_suffix (")")) { + // special key event for SKK-NICOLA + var strv = key[1:-1].split (" "); + if (strv.length != 2) { + throw new KeyEventFormatError.PARSE_FAILED ( + "usleep requires duration"); + } + name = strv[1]; + code = '\0'; + modifiers |= ModifierType.USLEEP_MASK; + } else if (key.has_prefix ("(") && key.has_suffix (")")) { var strv = key[1:-1].split (" "); int index = 0; for (; index < strv.length - 1; index++) { - if (strv[index] == "control") { - modifiers |= ModifierType.CONTROL_MASK; + if (strv[index] == "shift") { + _modifiers |= ModifierType.SHIFT_MASK; + } else if (strv[index] == "control") { + _modifiers |= ModifierType.CONTROL_MASK; } else if (strv[index] == "meta") { - modifiers |= ModifierType.META_MASK; + _modifiers |= ModifierType.META_MASK; } else if (strv[index] == "hyper") { - modifiers |= ModifierType.HYPER_MASK; + _modifiers |= ModifierType.HYPER_MASK; } else if (strv[index] == "super") { - modifiers |= ModifierType.SUPER_MASK; + _modifiers |= ModifierType.SUPER_MASK; } else if (strv[index] == "alt") { - modifiers |= ModifierType.MOD1_MASK; + _modifiers |= ModifierType.MOD1_MASK; } else if (strv[index] == "lshift") { - modifiers |= ModifierType.LSHIFT_MASK; + _modifiers |= ModifierType.LSHIFT_MASK; } else if (strv[index] == "rshift") { - modifiers |= ModifierType.RSHIFT_MASK; - } else if (strv[index] == "usleep") { - modifiers |= ModifierType.USLEEP_MASK; + _modifiers |= ModifierType.RSHIFT_MASK; } else if (strv[index] == "release") { - modifiers |= ModifierType.RELEASE_MASK; + _modifiers |= ModifierType.RELEASE_MASK; } else { throw new KeyEventFormatError.PARSE_FAILED ( "unknown modifier %s", strv[index]); } } - name = strv[index]; - code = name.char_count () == 1 ? name.get_char () : '\0'; - } - else { + // special key event for SKK-NICOLA + if (strv[index] == "lshift" || strv[index] == "rshift") { + name = strv[index]; + code = '\0'; + modifiers = ModifierType.NONE; + } else { + _keyval = KeyEventUtils.keyval_from_name (strv[index]); + if (_keyval == Keysyms.VoidSymbol) + throw new KeyEventFormatError.PARSE_FAILED ( + "unknown keyval %s", strv[index]); + name = KeyEventUtils.keyval_name (_keyval); + code = KeyEventUtils.keyval_unicode (_keyval); + modifiers = _modifiers; + } + } else if (key.has_prefix ("[") && key.has_suffix ("]") && + key.char_count () == 4) { + // special double key press events (SKK-NICOLA extension) + name = key; + code = '\0'; + modifiers = ModifierType.NONE; + } else { int index = key.last_index_of ("-"); + string? _name = null; if (index > 0) { // support only limited modifiers in this form string[] mods = key.substring (0, index).split ("-"); foreach (var mod in mods) { - if (mod == "C") { - modifiers |= ModifierType.CONTROL_MASK; + if (mod == "S") { + _modifiers |= ModifierType.SHIFT_MASK; + } else if (mod == "C") { + _modifiers |= ModifierType.CONTROL_MASK; } else if (mod == "A") { - modifiers |= ModifierType.MOD1_MASK; + _modifiers |= ModifierType.MOD1_MASK; } else if (mod == "M") { - modifiers |= ModifierType.META_MASK; + _modifiers |= ModifierType.META_MASK; } else if (mod == "G") { - modifiers |= ModifierType.MOD5_MASK; + _modifiers |= ModifierType.MOD5_MASK; } } - name = key.substring (index + 1); - code = name.char_count () == 1 ? name.get_char () : '\0'; + _name = key.substring (index + 1); } else { - modifiers = ModifierType.NONE; - name = key; - code = name.char_count () == 1 ? name.get_char () : '\0'; + _modifiers = ModifierType.NONE; + _name = key; } + _keyval = KeyEventUtils.keyval_from_name (_name); + if (_keyval == Keysyms.VoidSymbol) + throw new KeyEventFormatError.PARSE_FAILED ( + "unknown keyval %s", _name); + name = KeyEventUtils.keyval_name (_keyval); + code = KeyEventUtils.keyval_unicode (_keyval); + modifiers = _modifiers; } } @@ -215,37 +252,6 @@ namespace Skk { } } - // We can't use Entry here because of Vala bug: - // https://bugzilla.gnome.org/show_bug.cgi?id=684262 - struct CodeKeyvalEntry { - uint key; - unichar value; - } - - const CodeKeyvalEntry[] CODE_KEYVALS = { - { Keysyms.Tab, '\t' }, - { Keysyms.Return, '\n' }, - { Keysyms.BackSpace, '\b' } - }; - - struct NameKeyvalEntry { - uint key; - string value; - } - - const NameKeyvalEntry[] NAME_KEYVALS = { - { Keysyms.Up, "Up" }, - { Keysyms.Down, "Down" }, - { Keysyms.Left, "Left" }, - { Keysyms.Right, "Right" }, - { Keysyms.Page_Up, "Page_Up" }, - { Keysyms.KP_Page_Up, "Page_Up" }, - { Keysyms.Page_Down, "Page_Down" }, - { Keysyms.KP_Page_Down, "Page_Down" }, - { Keysyms.Muhenkan, "lshift" }, - { Keysyms.Henkan, "rshift" } - }; - /** * Create a key event from an X keysym and modifiers. * @@ -256,27 +262,9 @@ namespace Skk { */ public KeyEvent.from_x_keysym (uint keyval, ModifierType modifiers) throws KeyEventFormatError { - foreach (var entry in NAME_KEYVALS) { - if (entry.key == keyval) { - name = entry.value; - break; - } - } - foreach (var entry in CODE_KEYVALS) { - if (entry.key == keyval) { - code = entry.value; - break; - } - } - assert (name == null || code == '\0'); - if (name == null && code == '\0') { - if (0x20 <= keyval && keyval < 0x7F) { - code = (unichar) keyval; - } else { - throw new KeyEventFormatError.KEYSYM_NOT_FOUND ( - "unknown keysym %u", keyval); - } - } + name = KeyEventUtils.keyval_name (keyval); + code = KeyEventUtils.keyval_unicode (keyval); + this.modifiers = modifiers; } diff --git a/libskk/nicola.vala b/libskk/nicola.vala index 06cd665b..49c33622 100644 --- a/libskk/nicola.vala +++ b/libskk/nicola.vala @@ -91,11 +91,11 @@ namespace Skk { } static bool is_lshift (KeyEvent key) { - return key.name == "lshift"; + return key.name == "lshift" || key.name == "Muhenkan"; } static bool is_rshift (KeyEvent key) { - return key.name == "rshift"; + return key.name == "rshift" || key.name == "Henkan"; } static bool is_shift (KeyEvent key) { @@ -180,9 +180,9 @@ namespace Skk { } void apply_shift (KeyEvent s, KeyEvent c) { - if (s.name == "lshift") { + if (is_lshift (s)) { c.modifiers |= ModifierType.LSHIFT_MASK; - } else if (s.name == "rshift") { + } else if (is_rshift (s)) { c.modifiers |= ModifierType.RSHIFT_MASK; } } diff --git a/libskk/util.vala b/libskk/util.vala index 841eceb9..7a61d754 100644 --- a/libskk/util.vala +++ b/libskk/util.vala @@ -494,4 +494,70 @@ namespace Skk { _length = stat.st_size; } } + + abstract class KeyEventUtils : Object { + public static string? keyval_name (uint keyval) { + uint8[] buffer = new uint8[64]; + int ret = -1; + + do { + ret = Xkb.keysym_get_name ((uint32) keyval, buffer); + if (ret == -1) + return null; + if (ret < buffer.length) + return (string) buffer; + buffer = new uint8[buffer.length * 2]; + } while (ret >= buffer.length); + + return null; + } + + public static uint keyval_from_name (string name) { + // special cases for compatibilty with older libskk + if (name == " ") + name = "space"; + else if (name == "\t") + name = "Tab"; + else if (name == "\n") + name = "Return"; + else if (name == "\b") + name = "BackSpace"; + + var keysym = Xkb.keysym_from_name (name, Xkb.KeysymFlags.NO_FLAGS); + if (keysym == Xkb.Keysym.NoSymbol) { + // handle ASCII keyvals with differnet name (e.g. at, + // percent, etc.) + if (name.char_count () == 1) { + unichar code = name.get_char (); + if (0x20 <= code && code < 0x7F) + return code; + } + return Keysyms.VoidSymbol; + } + return (uint) keysym; + } + + public static unichar keyval_unicode (uint keyval) { + // handle ASCII keyvals with differnet name (e.g. at, + // percent, etc.) + if (0x20 <= keyval && keyval < 0x7F) + return keyval; + + // special case + if (keyval == Keysyms.yen) + return "\xc2\xa5".get_char (); + + uint8[] buffer = new uint8[8]; + int ret = -1; + + do { + ret = Xkb.keysym_to_utf8 ((uint32) keyval, buffer); + if (ret == 0) + return '\0'; + buffer = new uint8[buffer.length * 2]; + } while (ret == -1); + + return '\0'; + } + } } diff --git a/libskk/xkbcommon.vapi b/libskk/xkbcommon.vapi new file mode 100644 index 00000000..70b804ba --- /dev/null +++ b/libskk/xkbcommon.vapi @@ -0,0 +1,18 @@ +[CCode (cprefix = "xkb_", lower_case_cprefix = "xkb_", cheader_filename = "xkbcommon/xkbcommon.h")] +namespace Xkb +{ + namespace Keysym { + [CCode (cname = "XKB_KEY_NoSymbol")] + public const uint32 NoSymbol; + } + + public enum KeysymFlags { + [CCode (cname = "XKB_KEYSYM_NO_FLAGS")] + NO_FLAGS = 0, + CASE_INSENSITIVE = (1 << 0) + } + + public int keysym_get_name(uint32 keysym, [CCode (array_length_cname = "size", array_length_pos = 2.1, array_length_type = "size_t")] uint8[] buffer); + public uint32 keysym_from_name(string name, KeysymFlags flags); + public int keysym_to_utf8(uint32 keysym, [CCode (array_length_cname = "size", array_length_pos = 2.1, array_length_type = "size_t")] uint8[] buffer); +}