summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am18
-rw-r--r--README1
-rw-r--r--configure.ac4
-rw-r--r--external/imKStoUCS.c322
-rw-r--r--external/imKStoUCS.h8
-rw-r--r--src/input-private.h49
-rw-r--r--src/input-xkb.c919
-rw-r--r--src/input.c49
-rw-r--r--src/input.h19
-rw-r--r--tests/test_input.c57
10 files changed, 1433 insertions, 13 deletions
diff --git a/Makefile.am b/Makefile.am
index 7f96f27..98e30c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,8 @@ noinst_LTLIBRARIES = libkmscon-core.la
AM_CFLAGS = \
-Wall
AM_CPPFLAGS = \
- -I $(srcdir)/src
+ -I $(srcdir)/src \
+ -I $(srcdir)/external
AM_LDFLAGS = \
-Wl,--as-needed
@@ -27,6 +28,8 @@ libkmscon_core_la_SOURCES = \
src/eloop.c src/eloop.h \
src/vt.c src/vt.h \
src/input.c src/input.h \
+ src/input-xkb.c src/input-private.h \
+ external/imKStoUCS.c external\imKStoUCS.h \
src/vte.c src/vte.h \
src/terminal.c src/terminal.h
@@ -38,7 +41,8 @@ libkmscon_core_la_CPPFLAGS = \
$(OPENGL_CFLAGS) \
$(CAIRO_CFLAGS) \
$(PANGO_CFLAGS) \
- $(UDEV_CFLAGS)
+ $(UDEV_CFLAGS) \
+ $(XKBCOMMON_CFLAGS)
libkmscon_core_la_LIBADD = \
$(DRM_LIBS) \
$(EGL_LIBS) \
@@ -46,7 +50,8 @@ libkmscon_core_la_LIBADD = \
$(OPENGL_LIBS) \
$(CAIRO_LIBS) \
$(PANGO_LIBS) \
- $(UDEV_LIBS)
+ $(UDEV_LIBS) \
+ $(XKBCOMMON_LIBS)
kmscon_SOURCES = src/main.c
kmscon_LDADD = libkmscon-core.la
@@ -78,6 +83,11 @@ test_terminal_SOURCES = tests/test_terminal.c
test_terminal_LDADD = libkmscon-core.la
test_input_SOURCES = tests/test_input.c
-test_input_LDADD = libkmscon-core.la
+test_input_LDADD = \
+ libkmscon-core.la \
+ $(XKBCOMMON_LIBS)
+test_input_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(XKBCOMMON_CFLAGS)
dist_doc_DATA = README TODO
diff --git a/README b/README
index 3c809cd..1ba6f51 100644
--- a/README
+++ b/README
@@ -14,6 +14,7 @@ console.
- glib
- cairo
- pango
+ - libxkbcommon
== Install ==
To compile the kmscon binary, run the standard autotools commands:
diff --git a/configure.ac b/configure.ac
index 95d43d9..a193bb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,10 @@ PKG_CHECK_MODULES([UDEV], [libudev])
AC_SUBST(UDEV_CFLAGS)
AC_SUBST(UDEV_LIBS)
+PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon])
+AC_SUBST(XKBCOMMON_CFLAGS)
+AC_SUBST(XKBCOMMON_LIBS)
+
AC_MSG_CHECKING([whether to build with debugging on])
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug], [whether to build with debugging on)])],
diff --git a/external/imKStoUCS.c b/external/imKStoUCS.c
new file mode 100644
index 0000000..d19adf7
--- /dev/null
+++ b/external/imKStoUCS.c
@@ -0,0 +1,322 @@
+/*
+ * This is taken from the xlib:
+ * libX11/src/xlibil18n/imKStoUCS.c
+ */
+
+#include <X11/extensions/XKBcommon.h>
+
+static unsigned short const keysym_to_unicode_1a1_1ff[] = {
+ 0x0104, 0x02d8, 0x0141, 0x0000, 0x013d, 0x015a, 0x0000, /* 0x01a0-0x01a7 */
+ 0x0000, 0x0160, 0x015e, 0x0164, 0x0179, 0x0000, 0x017d, 0x017b, /* 0x01a8-0x01af */
+ 0x0000, 0x0105, 0x02db, 0x0142, 0x0000, 0x013e, 0x015b, 0x02c7, /* 0x01b0-0x01b7 */
+ 0x0000, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, /* 0x01b8-0x01bf */
+ 0x0154, 0x0000, 0x0000, 0x0102, 0x0000, 0x0139, 0x0106, 0x0000, /* 0x01c0-0x01c7 */
+ 0x010c, 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x0000, 0x010e, /* 0x01c8-0x01cf */
+ 0x0110, 0x0143, 0x0147, 0x0000, 0x0000, 0x0150, 0x0000, 0x0000, /* 0x01d0-0x01d7 */
+ 0x0158, 0x016e, 0x0000, 0x0170, 0x0000, 0x0000, 0x0162, 0x0000, /* 0x01d8-0x01df */
+ 0x0155, 0x0000, 0x0000, 0x0103, 0x0000, 0x013a, 0x0107, 0x0000, /* 0x01e0-0x01e7 */
+ 0x010d, 0x0000, 0x0119, 0x0000, 0x011b, 0x0000, 0x0000, 0x010f, /* 0x01e8-0x01ef */
+ 0x0111, 0x0144, 0x0148, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000, /* 0x01f0-0x01f7 */
+ 0x0159, 0x016f, 0x0000, 0x0171, 0x0000, 0x0000, 0x0163, 0x02d9 /* 0x01f8-0x01ff */
+};
+
+static unsigned short const keysym_to_unicode_2a1_2fe[] = {
+ 0x0126, 0x0000, 0x0000, 0x0000, 0x0000, 0x0124, 0x0000, /* 0x02a0-0x02a7 */
+ 0x0000, 0x0130, 0x0000, 0x011e, 0x0134, 0x0000, 0x0000, 0x0000, /* 0x02a8-0x02af */
+ 0x0000, 0x0127, 0x0000, 0x0000, 0x0000, 0x0000, 0x0125, 0x0000, /* 0x02b0-0x02b7 */
+ 0x0000, 0x0131, 0x0000, 0x011f, 0x0135, 0x0000, 0x0000, 0x0000, /* 0x02b8-0x02bf */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010a, 0x0108, 0x0000, /* 0x02c0-0x02c7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x02c8-0x02cf */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0120, 0x0000, 0x0000, /* 0x02d0-0x02d7 */
+ 0x011c, 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x015c, 0x0000, /* 0x02d8-0x02df */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010b, 0x0109, 0x0000, /* 0x02e0-0x02e7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x02e8-0x02ef */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0121, 0x0000, 0x0000, /* 0x02f0-0x02f7 */
+ 0x011d, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x015d /* 0x02f8-0x02ff */
+};
+
+static unsigned short const keysym_to_unicode_3a2_3fe[] = {
+ 0x0138, 0x0156, 0x0000, 0x0128, 0x013b, 0x0000, /* 0x03a0-0x03a7 */
+ 0x0000, 0x0000, 0x0112, 0x0122, 0x0166, 0x0000, 0x0000, 0x0000, /* 0x03a8-0x03af */
+ 0x0000, 0x0000, 0x0000, 0x0157, 0x0000, 0x0129, 0x013c, 0x0000, /* 0x03b0-0x03b7 */
+ 0x0000, 0x0000, 0x0113, 0x0123, 0x0167, 0x014a, 0x0000, 0x014b, /* 0x03b8-0x03bf */
+ 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012e, /* 0x03c0-0x03c7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0116, 0x0000, 0x0000, 0x012a, /* 0x03c8-0x03cf */
+ 0x0000, 0x0145, 0x014c, 0x0136, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x03d0-0x03d7 */
+ 0x0000, 0x0172, 0x0000, 0x0000, 0x0000, 0x0168, 0x016a, 0x0000, /* 0x03d8-0x03df */
+ 0x0101, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012f, /* 0x03e0-0x03e7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0117, 0x0000, 0x0000, 0x012b, /* 0x03e8-0x03ef */
+ 0x0000, 0x0146, 0x014d, 0x0137, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x03f0-0x03f7 */
+ 0x0000, 0x0173, 0x0000, 0x0000, 0x0000, 0x0169, 0x016b /* 0x03f8-0x03ff */
+};
+
+static unsigned short const keysym_to_unicode_4a1_4df[] = {
+ 0x3002, 0x3008, 0x3009, 0x3001, 0x30fb, 0x30f2, 0x30a1, /* 0x04a0-0x04a7 */
+ 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7, 0x30c3, /* 0x04a8-0x04af */
+ 0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad, /* 0x04b0-0x04b7 */
+ 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd, /* 0x04b8-0x04bf */
+ 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc, /* 0x04c0-0x04c7 */
+ 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de, /* 0x04c8-0x04cf */
+ 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9, /* 0x04d0-0x04d7 */
+ 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x309b, 0x309c /* 0x04d8-0x04df */
+};
+
+static unsigned short const keysym_to_unicode_590_5fe[] = {
+ 0x06f0, 0x06f1, 0x06f2, 0x06f3, 0x06f4, 0x06f5, 0x06f6, 0x06f7, /* 0x0590-0x0597 */
+ 0x06f8, 0x06f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x0598-0x059f */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x066a, 0x0670, 0x0679, /* 0x05a0-0x05a7 */
+
+ 0x067e, 0x0686, 0x0688, 0x0691, 0x060c, 0x0000, 0x06d4, 0x0000, /* 0x05ac-0x05af */
+ 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, /* 0x05b0-0x05b7 */
+ 0x0668, 0x0669, 0x0000, 0x061b, 0x0000, 0x0000, 0x0000, 0x061f, /* 0x05b8-0x05bf */
+ 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, /* 0x05c0-0x05c7 */
+ 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, /* 0x05c8-0x05cf */
+ 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, /* 0x05d0-0x05d7 */
+ 0x0638, 0x0639, 0x063a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x05d8-0x05df */
+ 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, /* 0x05e0-0x05e7 */
+ 0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, /* 0x05e8-0x05ef */
+ 0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0698, 0x06a4, /* 0x05f0-0x05f7 */
+ 0x06a9, 0x06af, 0x06ba, 0x06be, 0x06cc, 0x06d2, 0x06c1 /* 0x05f8-0x05fe */
+};
+
+static unsigned short keysym_to_unicode_680_6ff[] = {
+ 0x0492, 0x0496, 0x049a, 0x049c, 0x04a2, 0x04ae, 0x04b0, 0x04b2, /* 0x0680-0x0687 */
+ 0x04b6, 0x04b8, 0x04ba, 0x0000, 0x04d8, 0x04e2, 0x04e8, 0x04ee, /* 0x0688-0x068f */
+ 0x0493, 0x0497, 0x049b, 0x049d, 0x04a3, 0x04af, 0x04b1, 0x04b3, /* 0x0690-0x0697 */
+ 0x04b7, 0x04b9, 0x04bb, 0x0000, 0x04d9, 0x04e3, 0x04e9, 0x04ef, /* 0x0698-0x069f */
+ 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, /* 0x06a0-0x06a7 */
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0491, 0x045e, 0x045f, /* 0x06a8-0x06af */
+ 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, /* 0x06b0-0x06b7 */
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0490, 0x040e, 0x040f, /* 0x06b8-0x06bf */
+ 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, /* 0x06c0-0x06c7 */
+ 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, /* 0x06c8-0x06cf */
+ 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, /* 0x06d0-0x06d7 */
+ 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, /* 0x06d8-0x06df */
+ 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, /* 0x06e0-0x06e7 */
+ 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, /* 0x06e8-0x06ef */
+ 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, /* 0x06f0-0x06f7 */
+ 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a /* 0x06f8-0x06ff */
+};
+
+static unsigned short const keysym_to_unicode_7a1_7f9[] = {
+ 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, /* 0x07a0-0x07a7 */
+ 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, /* 0x07a8-0x07af */
+ 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, /* 0x07b0-0x07b7 */
+ 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x07b8-0x07bf */
+ 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, /* 0x07c0-0x07c7 */
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, /* 0x07c8-0x07cf */
+ 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, /* 0x07d0-0x07d7 */
+ 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x07d8-0x07df */
+ 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, /* 0x07e0-0x07e7 */
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, /* 0x07e8-0x07ef */
+ 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, /* 0x07f0-0x07f7 */
+ 0x03c8, 0x03c9 /* 0x07f8-0x07ff */
+};
+
+static unsigned short const keysym_to_unicode_8a4_8fe[] = {
+ 0x2320, 0x2321, 0x0000, 0x231c, /* 0x08a0-0x08a7 */
+ 0x231d, 0x231e, 0x231f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08a8-0x08af */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08b0-0x08b7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222b, /* 0x08b8-0x08bf */
+ 0x2234, 0x0000, 0x221e, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, /* 0x08c0-0x08c7 */
+ 0x2245, 0x2246, 0x0000, 0x0000, 0x0000, 0x0000, 0x21d2, 0x0000, /* 0x08c8-0x08cf */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221a, 0x0000, /* 0x08d0-0x08d7 */
+ 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228, /* 0x08d8-0x08df */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x08e0-0x08e7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, /* 0x08e8-0x08ef */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, /* 0x08f0-0x08f7 */
+ 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193 /* 0x08f8-0x08ff */
+};
+
+static unsigned short const keysym_to_unicode_9df_9f8[] = {
+ 0x2422, /* 0x09d8-0x09df */
+ 0x2666, 0x25a6, 0x2409, 0x240c, 0x240d, 0x240a, 0x0000, 0x0000, /* 0x09e0-0x09e7 */
+ 0x240a, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x2500, /* 0x09e8-0x09ef */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x251c, 0x2524, 0x2534, 0x252c, /* 0x09f0-0x09f7 */
+ 0x2502 /* 0x09f8-0x09ff */
+};
+
+static unsigned short const keysym_to_unicode_aa1_afe[] = {
+ 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, /* 0x0aa0-0x0aa7 */
+ 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, /* 0x0aa8-0x0aaf */
+ 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, /* 0x0ab0-0x0ab7 */
+ 0x2105, 0x0000, 0x0000, 0x2012, 0x2039, 0x2024, 0x203a, 0x0000, /* 0x0ab8-0x0abf */
+ 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, /* 0x0ac0-0x0ac7 */
+ 0x0000, 0x2122, 0x2120, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25ad, /* 0x0ac8-0x0acf */
+ 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, /* 0x0ad0-0x0ad7 */
+ 0x0000, 0x271d, 0x0000, 0x220e, 0x25c2, 0x2023, 0x25cf, 0x25ac, /* 0x0ad8-0x0adf */
+ 0x25e6, 0x25ab, 0x25ae, 0x25b5, 0x25bf, 0x2606, 0x2022, 0x25aa, /* 0x0ae0-0x0ae7 */
+ 0x25b4, 0x25be, 0x261a, 0x261b, 0x2663, 0x2666, 0x2665, 0x0000, /* 0x0ae8-0x0aef */
+ 0x2720, 0x2020, 0x2021, 0x2713, 0x2612, 0x266f, 0x266d, 0x2642, /* 0x0af0-0x0af7 */
+ 0x2640, 0x2121, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e /* 0x0af8-0x0aff */
+};
+
+/* none of the APL keysyms match the Unicode characters */
+
+static unsigned short const keysym_to_unicode_cdf_cfa[] = {
+ 0x2017, /* 0x0cd8-0x0cdf */
+ 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, /* 0x0ce0-0x0ce7 */
+ 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, /* 0x0ce8-0x0cef */
+ 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, /* 0x0cf0-0x0cf7 */
+ 0x05e8, 0x05e9, 0x05ea /* 0x0cf8-0x0cff */
+};
+
+static unsigned short const keysym_to_unicode_da1_df9[] = {
+ 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, /* 0x0da0-0x0da7 */
+ 0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, /* 0x0da8-0x0daf */
+ 0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, /* 0x0db0-0x0db7 */
+ 0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, /* 0x0db8-0x0dbf */
+ 0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, /* 0x0dc0-0x0dc7 */
+ 0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, /* 0x0dc8-0x0dcf */
+ 0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, /* 0x0dd0-0x0dd7 */
+ 0x0e38, 0x0e39, 0x0e3a, 0x0000, 0x0000, 0x0000, 0x0e3e, 0x0e3f, /* 0x0dd8-0x0ddf */
+ 0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, /* 0x0de0-0x0de7 */
+ 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0000, 0x0000, /* 0x0de8-0x0def */
+ 0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, /* 0x0df0-0x0df7 */
+ 0x0e58, 0x0e59 /* 0x0df8-0x0dff */
+};
+
+static unsigned short const keysym_to_unicode_ea0_eff[] = {
+ 0x0000, 0x1101, 0x1101, 0x11aa, 0x1102, 0x11ac, 0x11ad, 0x1103, /* 0x0ea0-0x0ea7 */
+ 0x1104, 0x1105, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4, 0x11b5, /* 0x0ea8-0x0eaf */
+ 0x11b6, 0x1106, 0x1107, 0x1108, 0x11b9, 0x1109, 0x110a, 0x110b, /* 0x0eb0-0x0eb7 */
+ 0x110c, 0x110d, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112, 0x1161, /* 0x0eb8-0x0ebf */
+ 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169, /* 0x0ec0-0x0ec7 */
+ 0x116a, 0x116b, 0x116c, 0x116d, 0x116e, 0x116f, 0x1170, 0x1171, /* 0x0ec8-0x0ecf */
+ 0x1172, 0x1173, 0x1174, 0x1175, 0x11a8, 0x11a9, 0x11aa, 0x11ab, /* 0x0ed0-0x0ed7 */
+ 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, /* 0x0ed8-0x0edf */
+ 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, /* 0x0ee0-0x0ee7 */
+ 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x0000, /* 0x0ee8-0x0eef */
+ 0x0000, 0x0000, 0x1140, 0x0000, 0x0000, 0x1159, 0x119e, 0x0000, /* 0x0ef0-0x0ef7 */
+ 0x11eb, 0x0000, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9, /* 0x0ef8-0x0eff */
+};
+
+static unsigned short keysym_to_unicode_12a1_12fe[] = {
+ 0x1e02, 0x1e03, 0x0000, 0x0000, 0x0000, 0x1e0a, 0x0000, /* 0x12a0-0x12a7 */
+ 0x1e80, 0x0000, 0x1e82, 0x1e0b, 0x1ef2, 0x0000, 0x0000, 0x0000, /* 0x12a8-0x12af */
+ 0x1e1e, 0x1e1f, 0x0000, 0x0000, 0x1e40, 0x1e41, 0x0000, 0x1e56, /* 0x12b0-0x12b7 */
+ 0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, /* 0x12b8-0x12bf */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12c0-0x12c7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12c8-0x12cf */
+ 0x0174, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1e6a, /* 0x12d0-0x12d7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0176, 0x0000, /* 0x12d8-0x12df */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12e0-0x12e7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x12e8-0x12ef */
+ 0x0175, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1e6b, /* 0x12f0-0x12f7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0177 /* 0x12f0-0x12ff */
+};
+
+static unsigned short const keysym_to_unicode_13bc_13be[] = {
+ 0x0152, 0x0153, 0x0178 /* 0x13b8-0x13bf */
+};
+
+static unsigned short keysym_to_unicode_14a1_14ff[] = {
+ 0x2741, 0x00a7, 0x0589, 0x0029, 0x0028, 0x00bb, 0x00ab, /* 0x14a0-0x14a7 */
+ 0x2014, 0x002e, 0x055d, 0x002c, 0x2013, 0x058a, 0x2026, 0x055c, /* 0x14a8-0x14af */
+ 0x055b, 0x055e, 0x0531, 0x0561, 0x0532, 0x0562, 0x0533, 0x0563, /* 0x14b0-0x14b7 */
+ 0x0534, 0x0564, 0x0535, 0x0565, 0x0536, 0x0566, 0x0537, 0x0567, /* 0x14b8-0x14bf */
+ 0x0538, 0x0568, 0x0539, 0x0569, 0x053a, 0x056a, 0x053b, 0x056b, /* 0x14c0-0x14c7 */
+ 0x053c, 0x056c, 0x053d, 0x056d, 0x053e, 0x056e, 0x053f, 0x056f, /* 0x14c8-0x14cf */
+ 0x0540, 0x0570, 0x0541, 0x0571, 0x0542, 0x0572, 0x0543, 0x0573, /* 0x14d0-0x14d7 */
+ 0x0544, 0x0574, 0x0545, 0x0575, 0x0546, 0x0576, 0x0547, 0x0577, /* 0x14d8-0x14df */
+ 0x0548, 0x0578, 0x0549, 0x0579, 0x054a, 0x057a, 0x054b, 0x057b, /* 0x14e0-0x14e7 */
+ 0x054c, 0x057c, 0x054d, 0x057d, 0x054e, 0x057e, 0x054f, 0x057f, /* 0x14e8-0x14ef */
+ 0x0550, 0x0580, 0x0551, 0x0581, 0x0552, 0x0582, 0x0553, 0x0583, /* 0x14f0-0x14f7 */
+ 0x0554, 0x0584, 0x0555, 0x0585, 0x0556, 0x0586, 0x2019, 0x0027, /* 0x14f8-0x14ff */
+};
+
+static unsigned short keysym_to_unicode_15d0_15f6[] = {
+ 0x10d0, 0x10d1, 0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, /* 0x15d0-0x15d7 */
+ 0x10d8, 0x10d9, 0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df, /* 0x15d8-0x15df */
+ 0x10e0, 0x10e1, 0x10e2, 0x10e3, 0x10e4, 0x10e5, 0x10e6, 0x10e7, /* 0x15e0-0x15e7 */
+ 0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed, 0x10ee, 0x10ef, /* 0x15e8-0x15ef */
+ 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6 /* 0x15f0-0x15f7 */
+};
+
+static unsigned short keysym_to_unicode_16a0_16f6[] = {
+ 0x0000, 0x0000, 0xf0a2, 0x1e8a, 0x0000, 0xf0a5, 0x012c, 0xf0a7, /* 0x16a0-0x16a7 */
+ 0xf0a8, 0x01b5, 0x01e6, 0x0000, 0x0000, 0x0000, 0x0000, 0x019f, /* 0x16a8-0x16af */
+ 0x0000, 0x0000, 0xf0b2, 0x1e8b, 0x01d1, 0xf0b5, 0x012d, 0xf0b7, /* 0x16b0-0x16b7 */
+ 0xf0b8, 0x01b6, 0x01e7, 0x0000, 0x0000, 0x01d2, 0x0000, 0x0275, /* 0x16b8-0x16bf */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x018f, 0x0000, /* 0x16c0-0x16c7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16c8-0x16cf */
+ 0x0000, 0x1e36, 0xf0d2, 0xf0d3, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16d0-0x16d7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16d8-0x16df */
+ 0x0000, 0x1e37, 0xf0e2, 0xf0e3, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16e0-0x16e7 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 0x16e8-0x16ef */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0259 /* 0x16f0-0x16f6 */
+};
+
+static unsigned short const keysym_to_unicode_1e9f_1eff[] = {
+ 0x0303,
+ 0x1ea0, 0x1ea1, 0x1ea2, 0x1ea3, 0x1ea4, 0x1ea5, 0x1ea6, 0x1ea7, /* 0x1ea0-0x1ea7 */
+ 0x1ea8, 0x1ea9, 0x1eaa, 0x1eab, 0x1eac, 0x1ead, 0x1eae, 0x1eaf, /* 0x1ea8-0x1eaf */
+ 0x1eb0, 0x1eb1, 0x1eb2, 0x1eb3, 0x1eb4, 0x1eb5, 0x1eb6, 0x1eb7, /* 0x1eb0-0x1eb7 */
+ 0x1eb8, 0x1eb9, 0x1eba, 0x1ebb, 0x1ebc, 0x1ebd, 0x1ebe, 0x1ebf, /* 0x1eb8-0x1ebf */
+ 0x1ec0, 0x1ec1, 0x1ec2, 0x1ec3, 0x1ec4, 0x1ec5, 0x1ec6, 0x1ec7, /* 0x1ec0-0x1ec7 */
+ 0x1ec8, 0x1ec9, 0x1eca, 0x1ecb, 0x1ecc, 0x1ecd, 0x1ece, 0x1ecf, /* 0x1ec8-0x1ecf */
+ 0x1ed0, 0x1ed1, 0x1ed2, 0x1ed3, 0x1ed4, 0x1ed5, 0x1ed6, 0x1ed7, /* 0x1ed0-0x1ed7 */
+ 0x1ed8, 0x1ed9, 0x1eda, 0x1edb, 0x1edc, 0x1edd, 0x1ede, 0x1edf, /* 0x1ed8-0x1edf */
+ 0x1ee0, 0x1ee1, 0x1ee2, 0x1ee3, 0x1ee4, 0x1ee5, 0x1ee6, 0x1ee7, /* 0x1ee0-0x1ee7 */
+ 0x1ee8, 0x1ee9, 0x1eea, 0x1eeb, 0x1eec, 0x1eed, 0x1eee, 0x1eef, /* 0x1ee8-0x1eef */
+ 0x1ef0, 0x1ef1, 0x0300, 0x0301, 0x1ef4, 0x1ef5, 0x1ef6, 0x1ef7, /* 0x1ef0-0x1ef7 */
+ 0x1ef8, 0x1ef9, 0x01a0, 0x01a1, 0x01af, 0x01b0, 0x0309, 0x0323 /* 0x1ef8-0x1eff */
+};
+
+static unsigned short const keysym_to_unicode_20a0_20ac[] = {
+ 0x20a0, 0x20a1, 0x20a2, 0x20a3, 0x20a4, 0x20a5, 0x20a6, 0x20a7, /* 0x20a0-0x20a7 */
+ 0x20a8, 0x20a9, 0x20aa, 0x20ab, 0x20ac /* 0x20a8-0x20af */
+};
+
+uint32_t
+KeysymToUcs4(uint32_t keysym)
+{
+ /* 'Unicode keysym' */
+ if ((keysym & 0xff000000) == 0x01000000)
+ return (keysym & 0x00ffffff);
+
+ if (keysym > 0 && keysym < 0x100)
+ return keysym;
+ else if (keysym > 0x1a0 && keysym < 0x200)
+ return keysym_to_unicode_1a1_1ff[keysym - 0x1a1];
+ else if (keysym > 0x2a0 && keysym < 0x2ff)
+ return keysym_to_unicode_2a1_2fe[keysym - 0x2a1];
+ else if (keysym > 0x3a1 && keysym < 0x3ff)
+ return keysym_to_unicode_3a2_3fe[keysym - 0x3a2];
+ else if (keysym > 0x4a0 && keysym < 0x4e0)
+ return keysym_to_unicode_4a1_4df[keysym - 0x4a1];
+ else if (keysym > 0x589 && keysym < 0x5ff)
+ return keysym_to_unicode_590_5fe[keysym - 0x590];
+ else if (keysym > 0x67f && keysym < 0x700)
+ return keysym_to_unicode_680_6ff[keysym - 0x680];
+ else if (keysym > 0x7a0 && keysym < 0x7fa)
+ return keysym_to_unicode_7a1_7f9[keysym - 0x7a1];
+ else if (keysym > 0x8a3 && keysym < 0x8ff)
+ return keysym_to_unicode_8a4_8fe[keysym - 0x8a4];
+ else if (keysym > 0x9de && keysym < 0x9f9)
+ return keysym_to_unicode_9df_9f8[keysym - 0x9df];
+ else if (keysym > 0xaa0 && keysym < 0xaff)
+ return keysym_to_unicode_aa1_afe[keysym - 0xaa1];
+ else if (keysym > 0xcde && keysym < 0xcfb)
+ return keysym_to_unicode_cdf_cfa[keysym - 0xcdf];
+ else if (keysym > 0xda0 && keysym < 0xdfa)
+ return keysym_to_unicode_da1_df9[keysym - 0xda1];
+ else if (keysym > 0xe9f && keysym < 0xf00)
+ return keysym_to_unicode_ea0_eff[keysym - 0xea0];
+ else if (keysym > 0x12a0 && keysym < 0x12ff)
+ return keysym_to_unicode_12a1_12fe[keysym - 0x12a1];
+ else if (keysym > 0x13bb && keysym < 0x13bf)
+ return keysym_to_unicode_13bc_13be[keysym - 0x13bc];
+ else if (keysym > 0x14a0 && keysym < 0x1500)
+ return keysym_to_unicode_14a1_14ff[keysym - 0x14a1];
+ else if (keysym > 0x15cf && keysym < 0x15f7)
+ return keysym_to_unicode_15d0_15f6[keysym - 0x15d0];
+ else if (keysym > 0x169f && keysym < 0x16f7)
+ return keysym_to_unicode_16a0_16f6[keysym - 0x16a0];
+ else if (keysym > 0x1e9e && keysym < 0x1f00)
+ return keysym_to_unicode_1e9f_1eff[keysym - 0x1e9f];
+ else if (keysym > 0x209f && keysym < 0x20ad)
+ return keysym_to_unicode_20a0_20ac[keysym - 0x20a0];
+ else
+ return 0;
+}
diff --git a/external/imKStoUCS.h b/external/imKStoUCS.h
new file mode 100644
index 0000000..8d9b690
--- /dev/null
+++ b/external/imKStoUCS.h
@@ -0,0 +1,8 @@
+/*
+ * This is taken from the xlib:
+ * libX11/src/xlibil18n/imKStoUCS.c
+ */
+
+#include <stdint.h>
+
+uint32_t KeysymToUcs4(uint32_t keysym);
diff --git a/src/input-private.h b/src/input-private.h
new file mode 100644
index 0000000..35c8899
--- /dev/null
+++ b/src/input-private.h
@@ -0,0 +1,49 @@
+/*
+ * kmscon - udev input hotplug and evdev handling
+ *
+ * Copyright (c) 2011 Ran Benita <ran234@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdint.h>
+
+#include "input.h"
+
+/*
+ * These are the values sent by the kernel in the /value/ field of the
+ * /input_event/ struct.
+ * See Documentation/input/event-codes.txt in the kernel tree.
+ */
+enum key_state {
+ KEY_STATE_RELEASED = 0,
+ KEY_STATE_PRESSED = 1,
+ KEY_STATE_REPEATED = 2,
+};
+
+int new_xkb_desc(const char *layout, const char *variant, const char *options,
+ struct xkb_desc **out);
+void free_xkb_desc(struct xkb_desc *desc);
+
+void reset_xkb_state(struct xkb_desc *desc, struct xkb_state *state,
+ int evdev_fd);
+
+bool process_evdev_key(struct xkb_desc *desc, struct xkb_state *state,
+ enum key_state key_state, uint16_t code,
+ struct kmscon_input_event *out);
diff --git a/src/input-xkb.c b/src/input-xkb.c
new file mode 100644
index 0000000..654c1c5
--- /dev/null
+++ b/src/input-xkb.c
@@ -0,0 +1,919 @@
+/*
+ * kmscon - translating key presses to input events using libxkbcommon
+ *
+ * Copyright (c) 2011 Ran Benita <ran234@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This mostly involves things the X server does normally and libxkbcommon
+ * doesn't provide us for free.
+ * This implements a minimal subset of XKB, mostly ignoring stuff like:
+ * - The protocol itself - we don't allow changing/querying and do everything
+ * at init.
+ * - Everything to do with pointing devices, buttons..
+ * - Indicators
+ * - Controls
+ * - Bells
+ * - Dead keys
+ * - All actions beside group- and modifier-related.
+ * - Behaviours
+ * - Geometries
+ * - And various tweaks to what we do support.
+ *
+ * Some references to understand what's going on:
+ *
+ * [Lib] The X Keyboard Extension: Library Specification
+ * http://www.x.org/releases/current/doc/libX11/specs/XKB/xkblib.html
+ * [Proto] The X Keyboard Extension: Protocol Specification
+ * http://www.x.org/releases/current/doc/kbproto/xkbproto.html
+ * [xserver] The X server source code dealing with xkb
+ * <xserver source root>/xkb/
+ * [xlib] The Xlib source code dealing with xkb and input methods
+ * <libX11 source root>/xkb/
+ * <libX11 source root>/modules/im/ximcp/
+ * [Pascal] Some XKB documentation by its maintainer (not the best english)
+ * http://pascal.tsu.ru/en/xkb/
+ * [Headers] Some XKB-related headers
+ * /usr/include/X11/extensions/XKBcommon.h
+ * /usr/include/X11/extensions/XKB.h
+ * /usr/include/X11/keysymdef.h
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+
+#include "input-private.h"
+#include "log.h"
+#include "imKStoUCS.h"
+
+/* internal procedures prototypes */
+static void init_compat(struct xkb_desc *desc);
+static void init_key_types(struct xkb_desc *desc);
+static void init_actions(struct xkb_desc *desc);
+static void init_indicators(struct xkb_desc *desc);
+static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode);
+static int allocate_key_acts(struct xkb_desc *desc, uint8_t keycode);
+static struct xkb_sym_interpret *find_sym_interpret(struct xkb_desc *desc,
+ uint32_t sym, uint16_t level, uint8_t key_modmap);
+static bool are_modifiers_matching(uint8_t mods, unsigned char match,
+ uint8_t to_mods);
+static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc, uint16_t vmods,
+ uint8_t real_mods);
+static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods);
+static void init_action(struct xkb_desc *desc, union xkb_action *action);
+static bool process_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum key_state key_state,
+ union xkb_action *action);
+static bool process_mod_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum key_state key_state,
+ struct xkb_mod_action *action);
+static bool process_group_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum key_state key_state,
+ struct xkb_group_action *action);
+static uint8_t wrap_group_keycode(struct xkb_desc *desc, KeyCode keycode,
+ int16_t group);
+static uint8_t wrap_group_control(struct xkb_desc *desc, int16_t group);
+static void update_effective_mods(struct xkb_desc *desc,
+ struct xkb_state *state);
+static void update_effective_group(struct xkb_desc *desc,
+ struct xkb_state *state);
+static struct xkb_indicator_map *find_indicator_map(struct xkb_desc *desc,
+ const char *indicator_name);
+
+/*
+ * Create a ready-to-use xkb description object. This is used in most places
+ * having to do with XKB.
+ */
+int new_xkb_desc(const char *layout, const char *variant, const char *options,
+ struct xkb_desc **out)
+{
+ struct xkb_desc *desc;
+
+ struct xkb_rule_names rmlvo = {
+ .rules = "evdev",
+ .model = "evdev",
+ .layout = layout,
+ .variant = variant,
+ .options = options,
+ };
+
+ desc = xkb_compile_keymap_from_rules(&rmlvo);
+ if (!desc)
+ return -EFAULT;
+
+ /* The order of these is important! */
+ init_compat(desc);
+ init_key_types(desc);
+ init_actions(desc);
+ init_indicators(desc);
+
+ *out = desc;
+ return 0;
+}
+
+void free_xkb_desc(struct xkb_desc *desc)
+{
+ if (!desc)
+ return;
+
+ /*
+ * XXX: Seems this doesn't really free everything, valgrind shows some
+ * big leaks from libxkbcommon. Hopefully we use just one up until we
+ * exit.
+ */
+ xkb_free_keymap(desc);
+}
+
+/*
+ * This mostly fills out the keycode-action mapping and puts the virtual
+ * modifier mappings in the right place.
+ */
+static void init_compat(struct xkb_desc *desc)
+{
+ /* If we use KeyCode it overflows. */
+ unsigned int keycode;
+
+ for (keycode = desc->min_key_code; keycode <= desc->max_key_code; keycode++)
+ init_compat_for_keycode(desc, keycode);
+}
+
+static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode)
+{
+ int ret;
+ int i, bit;
+ bool allocated;
+
+ uint32_t sym;
+ uint8_t group;
+ uint16_t level;
+ int num_groups;
+ int num_levels;
+
+ struct xkb_sym_interpret *si;
+ union xkb_action *action;
+ uint8_t key_modmap;
+
+ /*
+ * It's possible that someone had set some actions for the keycode
+ * through the symbols file, and so we shouldn't override with the
+ * compat. This is very uncommon though, only used by the breaks_caps
+ * option here.
+ */
+ if (XkbKeyHasActions(desc, keycode))
+ return 0;
+
+ key_modmap = desc->map->modmap[keycode];
+ num_groups = XkbKeyNumGroups(desc, keycode);
+
+ /*
+ * We need to track the sym level in order to support LevelOneOnly,
+ * which is used in some symbol interpretations.
+ */
+
+ allocated = false;
+ for (group=0, i=0; group < num_groups; group++) {
+ num_levels = XkbKeyGroupWidth(desc, keycode, group);
+
+ for (level=0; level < num_levels; level++) {
+ sym = XkbKeySymEntry(desc, keycode, level, group);
+ si = find_sym_interpret(desc, sym, level, key_modmap);
+
+ if (!si)
+ continue;
+
+ /* Set the key action mapping. */
+ if (si->act.type != XkbSA_NoAction) {
+ if (!allocated) {
+ ret = allocate_key_acts(desc, keycode);
+ if (ret)
+ return ret;
+ allocated = true;
+ }
+
+ action = XkbKeyActionEntry(desc, keycode,
+ level, group);
+ *action = (union xkb_action)si->act;
+ }
+
+ /* Set the key virtual modifier mapping. */
+ if (si->virtual_mod != XkbNoModifier)
+ desc->server->vmodmap[keycode] |=
+ 0x01<<si->virtual_mod;
+ }
+ }
+
+ /*
+ * Translate the virtual modifiers bound to this key to the real
+ * modifiers bound to this key.
+ * See [Lib] 17.4 for vmodmap and friends.
+ */
+ for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1)
+ if (bit&desc->server->vmodmap[keycode])
+ desc->server->vmods[i] |= desc->map->modmap[keycode];
+
+ return 0;
+}
+
+/*
+ * Allocate slots for a keycode in the key-action mapping array. xkbcommon
+ * doesn't do this by itself for actions from compat (that is almost all of
+ * them.
+ * See [xserver] XKBMAlloc.c:XkbResizeKeyActions() for the equivalent.
+ */
+static int allocate_key_acts(struct xkb_desc *desc, uint8_t keycode)
+{
+ unsigned short index;
+ union xkb_action *acts;
+ struct xkb_server_map *server;
+ int sym_count;
+ int new_needed;
+ unsigned short new_num_acts;
+ unsigned short new_size_acts;
+
+ server = desc->server;
+ sym_count = XkbKeyNumSyms(desc, keycode);
+
+ /*
+ * num_acts is the occupied slots, size_acts is the current total
+ * capacity.
+ */
+
+ if (XkbKeyHasActions(desc, keycode)) {
+ /* An array is already allocated for this key. */
+
+ /* index = server->key_acts[keycode]; */
+ } else if (server->num_acts + sym_count <= server->size_acts) {
+ /* There's enough left over space; use it. */
+
+ index = server->num_acts;
+ server->key_acts[keycode] = index;
+ server->num_acts += sym_count;
+ } else {
+ /* Need to allocate new space. */
+
+ index = server->num_acts;
+ new_num_acts = server->num_acts + sym_count;
+ new_needed = sym_count - (server->size_acts - new_num_acts);
+ /* Add some extra to avoid repeated reallocs. */
+ new_size_acts = server->size_acts + new_needed + 8;
+
+ acts = realloc(server->acts,
+ sizeof(union xkb_action) * new_size_acts);
+ if (!acts)
+ return -ENOMEM;
+
+ /* XkbSA_NoAction is 0x00 so we're good. */
+ memset(acts+index, 0, sym_count);
+ server->key_acts[keycode] = index;
+ server->num_acts = new_num_acts;
+ server->size_acts = new_size_acts;
+ server->acts = acts;
+ }
+
+ return 0;
+}
+
+/*
+ * Look for the most specific symbol interpretation for the keysym.
+ * See [xserver] XKBMisc.c:_XkbFindMatchingInterp() for the equivalent.
+ */
+static struct xkb_sym_interpret *find_sym_interpret(struct xkb_desc *desc,
+ uint32_t sym, uint16_t level, uint8_t key_modmap)
+{
+ int i;
+ struct xkb_sym_interpret *si;
+ struct xkb_sym_interpret *all_syms_si;
+
+ all_syms_si = NULL;
+
+ /*
+ * If we find a matching interpret specific to our symbol, we return
+ * it immediatly.
+ * If we didn't find any, we return the first matching all-catching
+ * interpret.
+ */
+
+ for (i=0; i < desc->compat->num_si; i++) {
+ si = &desc->compat->sym_interpret[i];
+
+ if (si->sym != sym && si->sym != 0)
+ continue;
+
+ /*
+ * If the interpret specified UseModMapMods=level1, the sym
+ * must be in the first level of its group.
+ */
+ if (si->match&XkbSI_LevelOneOnly && level != 0)
+ continue;
+
+ if (!are_modifiers_matching(si->mods, si->match, key_modmap))
+ continue;
+
+ if (si->sym != 0)
+ return si;
+ else if (all_syms_si == NULL)
+ all_syms_si = si;
+ }
+
+ return all_syms_si;
+}
+
+/*
+ * Check a sym interpret match condition.
+ * See [Lib] Table 18.1 for the logic.
+ */
+static bool are_modifiers_matching(uint8_t mods, unsigned char match,
+ uint8_t to_mods)
+{
+ switch (match&XkbSI_OpMask) {
+ case XkbSI_NoneOf:
+ return (mods&to_mods) == 0;
+ case XkbSI_AnyOfOrNone:
+ return true;
+ case XkbSI_AnyOf:
+ return (mods&to_mods) != 0;
+ case XkbSI_AllOf:
+ return (mods&to_mods) == mods;
+ case XkbSI_Exactly:
+ return mods == to_mods;
+ }
+
+ return false;
+}
+
+/*
+ * After we figured out the virtual mods from the compat component, we update
+ * the effective modifiers in the key_types component accordingly, because we
+ * use it extensively to find the correct shift level.
+ */
+static void init_key_types(struct xkb_desc *desc)
+{
+ int i, j;
+ struct xkb_key_type *type;
+ struct xkb_kt_map_entry *entry;
+ struct xkb_mods *mods;
+
+ for (i=0; i < desc->map->num_types; i++) {
+ type = &desc->map->types[i];
+ mods = &type->mods;
+
+ mods->mask = virtual_and_real_to_mask(desc, mods->vmods,
+ mods->real_mods);
+
+ for (j=0; j < type->map_count; j++) {
+ entry = &type->map[j];
+ mods = &entry->mods;
+
+ mods->mask = virtual_and_real_to_mask(desc,
+ mods->vmods, mods->real_mods);
+
+ /*
+ * If the entry's vmods are bound to something, it
+ * should be active.
+ */
+ if (virtual_to_real_mods(desc, mods->vmods))
+ entry->active = true;
+ }
+ }
+}
+
+/*
+ * Update the effective modifer mask of the various action objects after we
+ * initialized the virtual modifiers from compat. The only actions we change
+ * here are the mod_action types.
+ */
+static void init_actions(struct xkb_desc *desc)
+{
+ int i;
+ union xkb_action *action;
+ struct xkb_sym_interpret *si;
+
+ for (i=0; i < desc->server->num_acts; i++) {
+ action = &desc->server->acts[i];
+ init_action(desc, action);
+ }
+
+ for (i=0; i < desc->compat->num_si; i++) {
+ si = &desc->compat->sym_interpret[i];
+ action = (union xkb_action *)&si->act;
+ init_action(desc, action);
+ }
+}
+
+static void init_action(struct xkb_desc *desc, union xkb_action *action)
+{
+ struct xkb_mod_action *mod_act;
+
+ switch (action->type) {
+ case XkbSA_SetMods:
+ case XkbSA_LatchMods:
+ case XkbSA_LockMods:
+ mod_act = &action->mods;
+
+ mod_act->mask = virtual_and_real_to_mask(desc, mod_act->vmods,
+ mod_act->real_mods);
+ break;
+ }
+}
+
+/*
+ * Update to the effective modifier mask of the indicator objects. We use them
+ * to dicover which modifiers to match with which leds.
+ */
+static void init_indicators(struct xkb_desc *desc)
+{
+ int i;
+ struct xkb_indicator_map *im;
+ struct xkb_mods *mods;
+
+ for (i=0; i < XkbNumIndicators; i++) {
+ im = &desc->indicators->maps[i];
+ mods = &im->mods;
+
+ mods->mask = virtual_and_real_to_mask(desc, mods->vmods,
+ mods->real_mods);
+ }
+}
+
+static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods)
+{
+ int i;
+ uint32_t bit;
+ uint8_t mods;
+
+ mods = 0x00;
+
+ for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1)
+ if (vmods&bit)
+ mods |= desc->server->vmods[i];
+
+ return mods;
+}
+
+static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc,
+ uint16_t vmods, uint8_t real_mods)
+{
+ uint8_t mods = 0x00;
+
+ mods |= real_mods;
+ mods |= virtual_to_real_mods(desc, vmods);
+
+ return mods;
+}
+
+/*
+ * Call this when we regain control of the keyboard after losing it.
+ * We don't reset the locked group, this should survive a VT switch, etc. The
+ * locked modifiers are reset according to the keyboard LEDs.
+ */
+void reset_xkb_state(struct xkb_desc *desc, struct xkb_state *state,
+ int evdev_fd)
+{
+ int i;
+ /* One long should be enough (LED_MAX is currently 16). */
+ unsigned long leds, bit;
+ struct xkb_indicator_map *im;
+
+ state->group = 0;
+ state->base_group = 0;
+ state->latched_group = 0;
+
+ state->mods = 0;
+ state->base_mods = 0;
+ state->latched_mods = 0;
+ state->locked_mods = 0;
+
+ errno = 0;
+ ioctl(evdev_fd, EVIOCGLED(sizeof(leds)), &leds);
+ if (errno) {
+ log_warning("input: couldn't discover modifiers state: %m\n");
+ return;
+ }
+
+ /* The LED_* constants specifiy the bit location. */
+ for (i=0, bit=0x01; i < LED_MAX; i++, bit<<=1) {
+ if ((leds&bit) == 0)
+ continue;
+
+ im = NULL;
+ switch (i) {
+ case LED_NUML:
+ im = find_indicator_map(desc, "Num Lock");
+ break;
+ case LED_CAPSL:
+ im = find_indicator_map(desc, "Caps Lock");
+ break;
+ case LED_SCROLLL:
+ im = find_indicator_map(desc, "Scroll Lock");
+ break;
+ case LED_COMPOSE:
+ im = find_indicator_map(desc, "Compose");
+ break;
+ }
+
+ /* Only locked modifiers really matter here. */
+ if (im && im->which_mods == XkbIM_UseLocked)
+ state->locked_mods |= im->mods.mask;
+ }
+
+ update_effective_mods(desc, state);
+ update_effective_group(desc, state);
+}
+
+static struct xkb_indicator_map *find_indicator_map(struct xkb_desc *desc,
+ const char *indicator_name)
+{
+ int i;
+ uint32_t atom;
+
+ atom = xkb_intern_atom(indicator_name);
+
+ for (i=0; i < XkbNumIndicators; i++)
+ if (desc->names->indicators[i] == atom)
+ return &desc->indicators->maps[i];
+
+ return NULL;
+}
+
+/*
+ * The shift level to use for the keycode (together with the group) is
+ * determined by the modifier state. There are various "types" of ways to use
+ * the modifiers to shift the keycode; this is determined by the key_type
+ * object mapped to the (keycode, group) pair.
+ */
+uint16_t find_shift_level(struct xkb_desc *desc, KeyCode keycode,
+ uint8_t mods, uint8_t group)
+{
+ int i;
+ struct xkb_key_type *type;
+ struct xkb_kt_map_entry *entry;
+ uint8_t masked_mods;
+
+ type = XkbKeyKeyType(desc, keycode, group);
+
+ masked_mods = type->mods.mask & mods;
+
+ for (i=0; i < type->map_count; i++) {
+ entry = &type->map[i];
+
+ if (!entry->active)
+ continue;
+
+ /*
+ * Must march exactly after we masked it with the key_type's
+ * mask.
+ */
+ if (entry->mods.mask == masked_mods)
+ return entry->level;
+ }
+
+ /* The default is LevelOne. */
+ return 0;
+}
+
+/*
+ * This is the entry point to the XKB processing.
+ * We get an evdev scancode and the keyboard state, and should put out a
+ * proper input event.
+ * Some evdev input events shouldn't result in us sending an input event
+ * (e.g. a key release). The return value indicated whether the input_event
+ * was filled out or not.
+ */
+bool process_evdev_key(struct xkb_desc *desc, struct xkb_state *state,
+ enum key_state key_state, uint16_t code,
+ struct kmscon_input_event *out)
+{
+ KeyCode keycode;
+ uint8_t group;
+ uint16_t shift_level;
+ uint32_t sym;
+ union xkb_action *action;
+ bool state_changed;
+
+ keycode = code + desc->min_key_code;
+
+ /* Valid keycode. */
+ if (!XkbKeycodeInRange(desc, keycode))
+ return false;
+ /* Active keycode. */
+ if (XkbKeyNumSyms(desc, keycode) == 0)
+ return false;
+
+ group = wrap_group_keycode(desc, keycode, state->group);
+ shift_level = find_shift_level(desc, keycode, state->mods, group);
+ sym = XkbKeySymEntry(desc, keycode, shift_level, group);
+
+ state_changed = false;
+ if (key_state != KEY_STATE_REPEATED) {
+ action = XkbKeyActionEntry(desc, keycode, shift_level, group);
+ state_changed = process_action(desc, state, keycode,
+ key_state, action);
+ }
+
+ if (key_state != KEY_STATE_RELEASED) {
+ out->keycode = code;
+ out->keysym = sym;
+ out->modifiers = state->mods;
+ out->unicode = KeysymToUcs4(sym);
+
+ return true;
+ }
+
+ if (state_changed) {
+ /* Release latches. */
+ state->latched_mods = 0;
+ update_effective_mods(desc, state);
+ state->latched_group = 0;
+ update_effective_group(desc, state);
+ }
+
+ return false;
+}
+
+/*
+ * An action dispatcher. The return value indicates whether the keyboard state
+ * was changed.
+ */
+static bool process_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum key_state key_state,
+ union xkb_action *action)
+{
+ if (!action)
+ return false;
+
+ switch (action->type) {
+ case XkbSA_NoAction:
+ break;
+ case XkbSA_SetMods:
+ case XkbSA_LatchMods:
+ case XkbSA_LockMods:
+ return process_mod_action(desc, state, keycode, key_state,
+ &action->mods);
+ break;
+ case XkbSA_SetGroup:
+ case XkbSA_LatchGroup:
+ case XkbSA_LockGroup:
+ return process_group_action(desc, state, keycode, key_state,
+ &action->group);
+ break;
+ default:
+ /*
+ * Don't handle other actions.
+ * Note: There may be useful stuff here, like TerminateServer
+ * or SwitchScreen.
+ */
+ break;
+ }
+
+ return false;
+}
+
+/*
+ * Updates the modifiers state.
+ * See [Lib] Table 17.1 for logic.
+ * */
+static bool process_mod_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum key_state key_state,
+ struct xkb_mod_action *action)
+{
+ uint8_t mods;
+ uint8_t saved_mods;
+ uint8_t flags = action->flags;
+
+ if (action->flags&XkbSA_UseModMapMods)
+ mods = desc->map->modmap[keycode];
+ else
+ mods = action->mask;
+
+ /*
+ * FIXME: Some actions here should be conditioned "and no keys are
+ * physically depressed when this key is released".
+ */
+
+ switch (action->type) {
+ case XkbSA_SetMods:
+ if (key_state == KEY_STATE_PRESSED) {
+ state->base_mods |= mods;
+ } else if (key_state == KEY_STATE_RELEASED) {
+ state->base_mods &= ~mods;
+ if (flags&XkbSA_ClearLocks)
+ state->locked_mods &= ~mods;
+ }
+
+ break;
+ case XkbSA_LatchMods:
+ if (key_state == KEY_STATE_PRESSED) {
+ state->base_mods |= mods;
+ } else if (key_state == KEY_STATE_RELEASED) {
+ if (flags&XkbSA_ClearLocks) {
+ saved_mods = state->locked_mods;
+ state->locked_mods &= ~mods;
+ mods &= ~(mods&saved_mods);
+ }
+ if (flags&XkbSA_LatchToLock) {
+ saved_mods = mods;
+ mods = (mods&state->latched_mods);
+ state->locked_mods |= mods;
+ state->latched_mods &= ~mods;
+ mods = saved_mods&(~mods);
+ }
+ state->latched_mods |= mods;
+ }
+
+ break;
+ case XkbSA_LockMods:
+ /* We fake a little here and toggle both on and off on keypress. */
+ if (key_state == KEY_STATE_PRESSED) {
+ state->base_mods |= mods;
+ state->locked_mods ^= mods;
+ } else if (key_state == KEY_STATE_RELEASED) {
+ state->base_mods &= ~mods;
+ }
+
+ break;
+ }
+
+ update_effective_mods(desc, state);
+ return true;
+}
+
+/*
+ * Updates the group state.
+ * See [Lib] Table 17.4 for logic.
+ */
+static bool process_group_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum key_state key_state,
+ struct xkb_group_action *action)
+{
+ int16_t group = action->group;
+ uint8_t flags = action->flags;
+
+ /*
+ * Note: action->group is signed and may be negative if GroupAbsolute
+ * is not set. A group itself cannot be negative.
+ */
+
+ /*
+ * FIXME: Some actions here should be conditioned "and no keys are
+ * physically depressed when this key is released".
+ */
+
+ switch (action->type) {
+ case XkbSA_SetGroup:
+ if (key_state == KEY_STATE_PRESSED) {
+ if (flags&XkbSA_GroupAbsolute)
+ state->base_group = group;
+ else
+ state->base_group += action->group;
+ } else if (key_state == KEY_STATE_RELEASED) {
+ if (flags&XkbSA_ClearLocks)
+ state->locked_group = 0;
+ }
+
+ break;
+ case XkbSA_LatchGroup:
+ if (key_state == KEY_STATE_PRESSED) {
+ if (flags&XkbSA_GroupAbsolute)
+ state->base_group = group;
+ else
+ state->base_group += action->group;
+ } else if (key_state == KEY_STATE_RELEASED) {
+ if (flags&XkbSA_LatchToLock && state->latched_group) {
+ state->locked_group += group;
+ state->latched_group -= group;
+ } else {
+ state->latched_group += group;
+ }
+ }
+
+ break;
+ case XkbSA_LockGroup:
+ if (key_state == KEY_STATE_PRESSED) {
+ if (flags&XkbSA_GroupAbsolute)
+ state->locked_group = group;
+ else
+ state->locked_group += group;
+ }
+
+ break;
+ }
+
+ update_effective_group(desc, state);
+ return true;
+}
+
+/*
+ * Helper function for the wrap_group_* functions.
+ * See [Lib] 11.7.1 for the rules.
+ */
+static uint8_t wrap_group(int16_t group, int num_groups, uint8_t group_info)
+{
+ /* No need for wrapping. */
+ if (XkbIsLegalGroup(group) && group < num_groups)
+ return group;
+
+ switch (XkbOutOfRangeGroupAction(group_info)) {
+ case XkbWrapIntoRange:
+ /* This is the default, common method. */
+ return group%num_groups;
+
+ case XkbClampIntoRange:
+ /* This one seems to be unused. */
+ return num_groups-1;
+
+ case XkbRedirectIntoRange:
+ /* This one seems to be unused. */
+ group = XkbOutOfRangeGroupInfo(group_info);
+ /* If it's _still_ out of range, use the first group. */
+ if (group >= num_groups)
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Wrap an arbitrary group into a legal effective global group according to
+ * the GroupsWrap control.
+ * (Group actions mostly act on the group number in a relative manner [e.g.
+ * +1, -1]. So if we have N groups, the effective group is N-1, and we get a
+ * SetGroup +1, this tells us what to do.)
+ */
+static uint8_t wrap_group_control(struct xkb_desc *desc, int16_t group)
+{
+ int num_groups;
+ uint8_t group_info;
+
+ num_groups = XkbNumKbdGroups;
+ group_info = desc->ctrls->groups_wrap;
+
+ return wrap_group(group, num_groups, group_info);
+}
+
+/*
+ * Wrap the effective global group to a legal group for the keycode, according
+ * to the rule specified for the key.
+ * (Some groups keycodes may have more groups than others, and so the active
+ * effective group may make no sense for a certain keycode).
+ */
+static uint8_t wrap_group_keycode(struct xkb_desc *desc, KeyCode keycode,
+ int16_t group)
+{
+ int num_groups;
+ uint8_t group_info;
+
+ num_groups = XkbKeyNumGroups(desc, keycode);
+ group_info = XkbKeyGroupInfo(desc, keycode);
+
+ return wrap_group(group, num_groups, group_info);
+}
+
+/*
+ * Need to update the effective mods after any changes to the base, latched or
+ * locked mods.
+ */
+static void update_effective_mods(struct xkb_desc *desc,
+ struct xkb_state *state)
+{
+ state->mods = state->base_mods|state->latched_mods|state->locked_mods;
+}
+
+/*
+ * Need to update the effective group after any changes to the base, latched or
+ * locked group.
+ */
+static void update_effective_group(struct xkb_desc *desc,
+ struct xkb_state *state)
+{
+ int16_t group;
+
+ /* Bring what was changed back into range. */
+ state->base_group = wrap_group_control(desc, state->base_group);
+ state->locked_group = wrap_group_control(desc, state->locked_group);
+ state->latched_group = wrap_group_control(desc, state->latched_group);
+
+ /* Update the effective group. */
+ group = state->base_group + state->locked_group + state->latched_group;
+ state->group = wrap_group_control(desc, group);
+}
diff --git a/src/input.c b/src/input.c
index 3086b51..445834e 100644
--- a/src/input.c
+++ b/src/input.c
@@ -49,8 +49,9 @@
#include <unistd.h>
#include "eloop.h"
-#include "log.h"
#include "input.h"
+#include "input-private.h"
+#include "log.h"
enum input_state {
INPUT_ASLEEP,
@@ -65,6 +66,8 @@ struct kmscon_input_device {
int rfd;
char *devnode;
struct kmscon_fd *fd;
+
+ struct xkb_state xkb_state;
};
struct kmscon_input {
@@ -79,10 +82,30 @@ struct kmscon_input {
struct udev *udev;
struct udev_monitor *monitor;
struct kmscon_fd *monitor_fd;
+
+ struct xkb_desc *xkb_desc;
};
static void remove_device(struct kmscon_input *input, const char *node);
+static void notify_key(struct kmscon_input_device *device,
+ uint16_t type, uint16_t code, int32_t value)
+{
+ struct kmscon_input_event ev;
+ bool has_event;
+ struct kmscon_input *input;
+
+ if (type != EV_KEY)
+ return;
+
+ input = device->input;
+ has_event = process_evdev_key(input->xkb_desc, &device->xkb_state,
+ value, code, &ev);
+
+ if (has_event)
+ input->cb(input, &ev, input->data);
+}
+
static void device_data_arrived(struct kmscon_fd *fd, int mask, void *data)
{
int i;
@@ -109,8 +132,8 @@ static void device_data_arrived(struct kmscon_fd *fd, int mask, void *data)
} else {
n = len / sizeof(*ev);
for (i = 0; i < n; i++)
- input->cb(input, ev[i].type, ev[i].code,
- ev[i].value, input->data);
+ notify_key(device, ev[i].type, ev[i].code,
+ ev[i].value);
}
}
}
@@ -132,6 +155,10 @@ int kmscon_input_device_wake_up(struct kmscon_input_device *device)
return -errno;
}
+ /* this rediscovers the xkb state if sth changed during sleep */
+ reset_xkb_state(device->input->xkb_desc, &device->xkb_state,
+ device->rfd);
+
ret = kmscon_eloop_new_fd(device->input->eloop, &device->fd,
device->rfd, KMSCON_READABLE, device_data_arrived, device);
if (ret) {
@@ -214,6 +241,11 @@ int kmscon_input_new(struct kmscon_input **out)
int ret;
struct kmscon_input *input;
+ /* TODO: Make configurable */
+ static const char *layout = "us,ru,il";
+ static const char *variant = "intl,,biblical";
+ static const char *options = "grp:menu_toggle";
+
if (!out)
return -EINVAL;
@@ -227,11 +259,17 @@ int kmscon_input_new(struct kmscon_input **out)
input->ref = 1;
input->state = INPUT_ASLEEP;
+ ret = new_xkb_desc(layout, variant, options, &input->xkb_desc);
+ if (ret) {
+ log_warning("input: cannot create xkb description\n");
+ goto err_free;
+ }
+
input->udev = udev_new();
if (!input->udev) {
log_warning("input: cannot create udev object\n");
ret = -EFAULT;
- goto err_free;
+ goto err_xkb;
}
input->monitor = udev_monitor_new_from_netlink(input->udev, "udev");
@@ -263,6 +301,8 @@ err_monitor:
udev_monitor_unref(input->monitor);
err_udev:
udev_unref(input->udev);
+err_xkb:
+ free_xkb_desc(input->xkb_desc);
err_free:
free(input);
return ret;
@@ -287,6 +327,7 @@ void kmscon_input_unref(struct kmscon_input *input)
kmscon_input_disconnect_eloop(input);
udev_monitor_unref(input->monitor);
udev_unref(input->udev);
+ free_xkb_desc(input->xkb_desc);
free(input);
log_debug("input: destroying input object\n");
}
diff --git a/src/input.h b/src/input.h
index d77626f..1666ef0 100644
--- a/src/input.h
+++ b/src/input.h
@@ -45,13 +45,26 @@
* reacting to VT changes.
*/
+#ifndef KMSCON_INPUT_H
+#define KMSCON_INPUT_H
+
#include <inttypes.h>
#include <stdbool.h>
+#include <X11/extensions/XKBcommon.h>
+#include <X11/keysym.h>
#include "eloop.h"
struct kmscon_input;
-typedef void (*kmscon_input_cb) (struct kmscon_input *input, uint16_t type,
- uint16_t code, int32_t value, void *data);
+
+struct kmscon_input_event {
+ uint16_t keycode; /* linux keycode - KEY_* - linux/input.h */
+ uint32_t keysym; /* X keysym - XK_* - X11/keysym.h */
+ uint8_t modifiers; /* xkbcommon modifiers - XKB_COMMON_*_MASK */
+ uint32_t unicode; /* UCS-4 unicode value, 0 if none */
+};
+
+typedef void (*kmscon_input_cb) (struct kmscon_input *input,
+ struct kmscon_input_event *ev, void *data);
int kmscon_input_new(struct kmscon_input **out);
void kmscon_input_ref(struct kmscon_input *input);
@@ -64,3 +77,5 @@ void kmscon_input_disconnect_eloop(struct kmscon_input *input);
void kmscon_input_sleep(struct kmscon_input *input);
void kmscon_input_wake_up(struct kmscon_input *input);
bool kmscon_input_is_asleep(struct kmscon_input *input);
+
+#endif /* KMSCON_INPUT_H */
diff --git a/tests/test_input.c b/tests/test_input.c
index 265db3b..9e6fbdc 100644
--- a/tests/test_input.c
+++ b/tests/test_input.c
@@ -25,12 +25,14 @@
#include <errno.h>
#include <linux/input.h>
+#include <locale.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <wchar.h>
#include "eloop.h"
#include "input.h"
@@ -57,10 +59,53 @@ static void sig_quit(struct kmscon_signal *sig, int signum, void *data)
}
}
-static void input_arrived(struct kmscon_input *input, uint16_t type,
- uint16_t code, int32_t value, void *data)
+static void print_modifiers(uint8_t mods)
{
- printf("got (type %x) (code %x) (value %x)\n", type, code, value);
+ if (mods & XKB_COMMON_SHIFT_MASK)
+ printf("SHIFT ");
+ if (mods & XKB_COMMON_LOCK_MASK)
+ printf("LOCK ");
+ if (mods & XKB_COMMON_CONTROL_MASK)
+ printf("CONTROL ");
+ if (mods & XKB_COMMON_MOD1_MASK)
+ printf("MOD1 ");
+ if (mods & XKB_COMMON_MOD2_MASK)
+ printf("MOD2 ");
+ if (mods & XKB_COMMON_MOD3_MASK)
+ printf("MOD3 ");
+ if (mods & XKB_COMMON_MOD4_MASK)
+ printf("MOD4 ");
+ if (mods & XKB_COMMON_MOD5_MASK)
+ printf("MOD5 ");
+ printf("\n");
+}
+
+static void input_arrived(struct kmscon_input *input,
+ struct kmscon_input_event *ev, void *data)
+{
+ int len;
+ char s[16];
+ char utf8[MB_CUR_MAX + 1];
+
+ if (ev->unicode == 0) {
+ xkb_keysym_to_string(ev->keysym, s, sizeof(s));
+ printf("sym %s ", s);
+ } else {
+ /*
+ * Just a proof-of-concept hack. This works because glibc uses
+ * UTF-32 (= UCS-4) as the internal wchar_t encoding.
+ */
+ len = wctomb(utf8, (wchar_t)ev->unicode);
+ if (len <= 0) {
+ log_info("Bad unicode char\n");
+ return;
+ } else {
+ utf8[len] = '\0';
+ }
+
+ printf("utf8 %s ", utf8);
+ }
+ print_modifiers(ev->modifiers);
}
int main(int argc, char **argv)
@@ -70,6 +115,12 @@ int main(int argc, char **argv)
struct kmscon_input *input;
struct kmscon_signal *sigint, *sigquit;
+ if (!setlocale(LC_ALL, "en_US.UTF-8")) {
+ log_err("Cannot set locale: %m\n");
+ ret = -EFAULT;
+ goto err_out;
+ }
+
ret = kmscon_eloop_new(&loop);
if (ret) {
log_err("Cannot create eloop\n");