diff options
-rw-r--r-- | Makefile.am | 18 | ||||
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | external/imKStoUCS.c | 322 | ||||
-rw-r--r-- | external/imKStoUCS.h | 8 | ||||
-rw-r--r-- | src/input-private.h | 49 | ||||
-rw-r--r-- | src/input-xkb.c | 919 | ||||
-rw-r--r-- | src/input.c | 49 | ||||
-rw-r--r-- | src/input.h | 19 | ||||
-rw-r--r-- | tests/test_input.c | 57 |
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 @@ -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"); |