diff --git a/Makefile.am b/Makefile.am index 21ca61e..02ac51f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,11 +28,19 @@ 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_xkb.h \ - external/imKStoUCS.c external\imKStoUCS.h \ src/vte.c src/vte.h \ src/terminal.c src/terminal.h +if USE_XKBCOMMON +libkmscon_core_la_SOURCES += \ + src/kbd_xkb.c src/kbd.h \ + external/imKStoUCS.c external\imKStoUCS.h +else +libkmscon_core_la_SOURCES += \ + src/kbd_dumb.c src/kbd.h \ + external/imKStoUCS.c external\imKStoUCS.h +endif + if USE_PANGO libkmscon_core_la_SOURCES += \ src/font_pango.c src/font.h @@ -50,6 +58,7 @@ libkmscon_core_la_CPPFLAGS = \ $(PANGO_CFLAGS) \ $(UDEV_CFLAGS) \ $(FREETYPE2_CFLAGS) \ + $(XPROTO_CFLAGS) \ $(XKBCOMMON_CFLAGS) \ $(GLIB_CFLAGS) libkmscon_core_la_LIBADD = \ @@ -60,6 +69,7 @@ libkmscon_core_la_LIBADD = \ $(PANGO_LIBS) \ $(UDEV_LIBS) \ $(FREETYPE2_LIBS) \ + $(XPROTO_LIBS) \ $(XKBCOMMON_LIBS) \ $(GLIB_LIBS) @@ -92,11 +102,6 @@ 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 \ - $(XKBCOMMON_LIBS) -test_input_CPPFLAGS = \ - $(AM_CPPFLAGS) \ - $(XKBCOMMON_CFLAGS) +test_input_LDADD = libkmscon-core.la EXTRA_DIST = README TODO COPYING diff --git a/README b/README index 375888f..9c57407 100644 --- a/README +++ b/README @@ -9,7 +9,9 @@ console. - mesa: providing an OpenGL implementation (must be compiled with EGL, gbm and GL libraries) - udev: providing input device hotplug - - libxkbcommon: keyboard handling + - xproto (build time dependency): definition of key symbols + - libxkbcommon: keyboard handling (optional but strongly recommended) + Without libxkbcommon, basic US-ASCII input is provided. - glib: only for Unicode handling - One of: - freetype2: drawing generic text diff --git a/configure.ac b/configure.ac index d4db1e3..3d14c33 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,12 @@ PKG_CHECK_MODULES([UDEV], [libudev]) AC_SUBST(UDEV_CFLAGS) AC_SUBST(UDEV_LIBS) -PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon]) +PKG_CHECK_MODULES([XPROTO], [xproto]) +AC_SUBST(XPROTO_CFLAGS) +AC_SUBST(XPROTO_LIBS) + +PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon], + [have_xkbcommon=yes], [have_xkbcommon=no]) AC_SUBST(XKBCOMMON_CFLAGS) AC_SUBST(XKBCOMMON_LIBS) @@ -58,6 +63,18 @@ PKG_CHECK_MODULES([PANGO], [pango pangocairo cairo], AC_SUBST(PANGO_CFLAGS) AC_SUBST(PANGO_LIBS) +AC_MSG_CHECKING([whether to use xkbcommon keyboard backend]) +AC_ARG_ENABLE([xkbcommon], + [AS_HELP_STRING([--disable-xkbcommon], [disable xkbcommon keyboard backend])]) +if test x$enable_xkbcommon != xno ; then + if test x$enable_xkbcommon = xyes -a x$have_xkbcommon = xno ; then + AC_ERROR([--enable-xkbcommon given but library not found]) + fi + enable_xkbcommon=$have_xkbcommon +fi +AM_CONDITIONAL([USE_XKBCOMMON], [test x$enable_xkbcommon = xyes]) +AC_MSG_RESULT([$enable_xkbcommon]) + AC_MSG_CHECKING([whether to use pango font backend]) AC_ARG_ENABLE([pango], [AS_HELP_STRING([--enable-pango], [whether to use pango font backend])], diff --git a/src/input.c b/src/input.c index 9d239ee..3ec63c3 100644 --- a/src/input.c +++ b/src/input.c @@ -50,7 +50,7 @@ #include "eloop.h" #include "input.h" -#include "input_xkb.h" +#include "kbd.h" #include "log.h" enum input_state { @@ -67,7 +67,7 @@ struct kmscon_input_device { char *devnode; struct kmscon_fd *fd; - struct xkb_state xkb_state; + struct kmscon_kbd *kbd; }; struct kmscon_input { @@ -83,7 +83,7 @@ struct kmscon_input { struct udev_monitor *monitor; struct kmscon_fd *monitor_fd; - struct xkb_desc *xkb_desc; + struct kmscon_kbd_desc *desc; }; static void remove_device(struct kmscon_input *input, const char *node); @@ -91,18 +91,20 @@ 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) { + int ret; struct kmscon_input_event ev; - bool has_event; struct kmscon_input *input; if (type != EV_KEY) return; input = device->input; - has_event = kmscon_xkb_process_evdev_key(input->xkb_desc, - &device->xkb_state, value, code, &ev); + ret = kmscon_kbd_process_key(device->kbd, value, code, &ev); - if (has_event) + if (ret && ret != -ENOKEY) + return; + + if (ret != -ENOKEY && input->cb) input->cb(input, &ev, input->data); } @@ -155,9 +157,8 @@ int kmscon_input_device_wake_up(struct kmscon_input_device *device) return -errno; } - /* this rediscovers the xkb state if sth changed during sleep */ - kmscon_xkb_reset_state(device->input->xkb_desc, &device->xkb_state, - device->rfd); + /* this rediscovers the keyboard state if sth changed during sleep */ + kmscon_kbd_reset(device->kbd, device->rfd); ret = kmscon_eloop_new_fd(device->input->eloop, &device->fd, device->rfd, KMSCON_READABLE, device_data_arrived, device); @@ -187,6 +188,7 @@ void kmscon_input_device_sleep(struct kmscon_input_device *device) static int kmscon_input_device_new(struct kmscon_input_device **out, struct kmscon_input *input, const char *devnode) { + int ret; struct kmscon_input_device *device; if (!out || !input) @@ -207,6 +209,13 @@ static int kmscon_input_device_new(struct kmscon_input_device **out, return -ENOMEM; } + ret = kmscon_kbd_new(&device->kbd, input->desc); + if (ret) { + free(device->devnode); + free(device); + return ret; + } + device->input = input; device->rfd = -1; @@ -214,13 +223,13 @@ static int kmscon_input_device_new(struct kmscon_input_device **out, return 0; } -static void kmscon_input_device_ref(struct kmscon_input_device *device) -{ - if (!device) - return; +/* static void kmscon_input_device_ref(struct kmscon_input_device *device) */ +/* { */ +/* if (!device) */ +/* return; */ - ++device->ref; -} +/* ++device->ref; */ +/* } */ static void kmscon_input_device_unref(struct kmscon_input_device *device) { @@ -231,6 +240,7 @@ static void kmscon_input_device_unref(struct kmscon_input_device *device) return; kmscon_input_device_sleep(device); + kmscon_kbd_unref(device->kbd); log_debug("input: destroying input device %s\n", device->devnode); free(device->devnode); free(device); @@ -260,7 +270,7 @@ int kmscon_input_new(struct kmscon_input **out) variant = getenv("KMSCON_XKB_VARIANT") ?: ""; options = getenv("KMSCON_XKB_OPTIONS") ?: ""; - ret = kmscon_xkb_new_desc(layout, variant, options, &input->xkb_desc); + ret = kmscon_kbd_desc_new(&input->desc, layout, variant, options); if (ret) { log_warning("input: cannot create xkb description\n"); goto err_free; @@ -303,7 +313,7 @@ err_monitor: err_udev: udev_unref(input->udev); err_xkb: - kmscon_xkb_free_desc(input->xkb_desc); + kmscon_kbd_desc_unref(input->desc); err_free: free(input); return ret; @@ -328,7 +338,7 @@ void kmscon_input_unref(struct kmscon_input *input) kmscon_input_disconnect_eloop(input); udev_monitor_unref(input->monitor); udev_unref(input->udev); - kmscon_xkb_free_desc(input->xkb_desc); + kmscon_kbd_desc_unref(input->desc); free(input); log_debug("input: destroying input object\n"); } diff --git a/src/input.h b/src/input.h index 579716f..9b70e31 100644 --- a/src/input.h +++ b/src/input.h @@ -77,17 +77,6 @@ struct kmscon_input_event { typedef void (*kmscon_input_cb) (struct kmscon_input *input, struct kmscon_input_event *ev, void *data); -/* - * 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 kmscon_key_state { - KMSCON_KEY_RELEASED = 0, - KMSCON_KEY_PRESSED = 1, - KMSCON_KEY_REPEATED = 2, -}; - int kmscon_input_new(struct kmscon_input **out); void kmscon_input_ref(struct kmscon_input *input); void kmscon_input_unref(struct kmscon_input *input); diff --git a/src/input_xkb.h b/src/input_xkb.h deleted file mode 100644 index b5b4816..0000000 --- a/src/input_xkb.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * kmscon - udev input hotplug and evdev handling - * - * Copyright (c) 2011 Ran Benita - * - * 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. - */ - -#ifndef KMSCON_INPUT_XKB_H -#define KMSCON_INPUT_XKB_H - -#include -#include -#include "input.h" - -int kmscon_xkb_new_desc(const char *layout, const char *variant, - const char *options, - struct xkb_desc **out); -void kmscon_xkb_free_desc(struct xkb_desc *desc); - -void kmscon_xkb_reset_state(struct xkb_desc *desc, - struct xkb_state *state, - int evdev_fd); - -bool kmscon_xkb_process_evdev_key(struct xkb_desc *desc, - struct xkb_state *state, - enum kmscon_key_state key_state, - uint16_t code, - struct kmscon_input_event *out); - -#endif /* KMSCON_INPUT_XKB_H */ diff --git a/src/kbd.h b/src/kbd.h new file mode 100644 index 0000000..f9915ac --- /dev/null +++ b/src/kbd.h @@ -0,0 +1,88 @@ +/* + * kmscon - translating key presses to input events + * + * Copyright (c) 2012 Ran Benita + * + * 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 defines the API the keyboard backends need to implement. The main + * function of a keyboard backend is to translate a kernel input event into a + * kmscon_input_event (see kmscon_kbd_process_key function). + * + * The two exported object are a "keyboard" object and a "keyboard + * description" object. The keyboard object holds all the device specific + * private state (e.g. active groups, modifiers). The description object + * holds all the global information (e.g. layouts, mapping tables). + */ + +#ifndef KMSCON_KBD_H +#define KMSCON_KBD_H + +#include +#include "input.h" + +struct kmscon_kbd_desc; +struct kmscon_kbd; + +/* + * 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 kmscon_key_state { + KMSCON_KEY_RELEASED = 0, + KMSCON_KEY_PRESSED = 1, + KMSCON_KEY_REPEATED = 2, +}; + +int kmscon_kbd_desc_new(struct kmscon_kbd_desc **out, const char *layout, + const char *variant, const char *options); +void kmscon_kbd_desc_ref(struct kmscon_kbd_desc *desc); +void kmscon_kbd_desc_unref(struct kmscon_kbd_desc *desc); + +int kmscon_kbd_new(struct kmscon_kbd **out, struct kmscon_kbd_desc *desc); +void kmscon_kbd_ref(struct kmscon_kbd *state); +void kmscon_kbd_unref(struct kmscon_kbd *state); + +/* + * This resets the keyboard state in case it got out of sync. It's mainly used + * to sync our notion of the keyboard state with what the keyboard LEDs show. + */ +void kmscon_kbd_reset(struct kmscon_kbd *kbd, int evdev_fd); + +/* + * This is the entry point to the keyboard 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): + * - If the event was filled out, 0 is returned. + * - Otherwise, if there was no error, -ENOKEY is returned. + */ +int kmscon_kbd_process_key(struct kmscon_kbd *kbd, + enum kmscon_key_state key_state, + uint16_t code, + struct kmscon_input_event *out); + +void kmscon_kbd_keysym_to_string(uint32_t keysym, char *str, size_t size); + +#endif /* KMSCON_KBD_H */ diff --git a/src/kbd_dumb.c b/src/kbd_dumb.c new file mode 100644 index 0000000..9938408 --- /dev/null +++ b/src/kbd_dumb.c @@ -0,0 +1,472 @@ +/* + * kmscon - translating key presses to input events using keycodes + * + * Copyright (c) 2012 Ran Benita + * + * 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 +#include +#include +#include +#include +#include + +#include "input.h" +#include "kbd.h" +#include "log.h" +#include "imKStoUCS.h" + +/* + * This is a very "dumb" and simple fallback backend for keycodes + * interpretation. It uses direct mapping from kernel keycodes to X keysyms + * according to a basic US PC keyboard. It is not configurable and does not + * support unicode or other languages. + * + * The key interpretation is affected by the following modifiers: Numlock, + * Shift, Capslock, and "Normal" (no mofifiers) in that order. If a keycode is + * not affected by one of these depressed modifiers, the next matching one is + * attempted. + */ + +struct kmscon_kbd_desc { + unsigned long ref; + + /* + * There is no need for this structure here currently. It can contain + * pointers to alternative keytabs and modmaps, if we ever want this + * backend to support different languages, etc. + */ +}; + +struct kmscon_kbd { + unsigned long ref; + struct kmscon_kbd_desc *desc; + + unsigned int mods; +}; + +/* + * These tables do not contain all possible keys from linux/input.h. + * If a keycode does not appear, it is mapped to keysym 0 and regarded as not + * found. + */ + +static const uint32_t keytab_normal[] = { + [KEY_ESC] = XK_Escape, + [KEY_1] = XK_1, + [KEY_2] = XK_2, + [KEY_3] = XK_3, + [KEY_4] = XK_4, + [KEY_5] = XK_5, + [KEY_6] = XK_6, + [KEY_7] = XK_7, + [KEY_8] = XK_8, + [KEY_9] = XK_9, + [KEY_0] = XK_0, + [KEY_MINUS] = XK_minus, + [KEY_EQUAL] = XK_equal, + [KEY_BACKSPACE] = XK_BackSpace, + [KEY_TAB] = XK_Tab, + [KEY_Q] = XK_q, + [KEY_W] = XK_w, + [KEY_E] = XK_e, + [KEY_R] = XK_r, + [KEY_T] = XK_t, + [KEY_Y] = XK_y, + [KEY_U] = XK_u, + [KEY_I] = XK_i, + [KEY_O] = XK_o, + [KEY_P] = XK_p, + [KEY_LEFTBRACE] = XK_bracketleft, + [KEY_RIGHTBRACE] = XK_bracketright, + [KEY_ENTER] = XK_Return, + [KEY_LEFTCTRL] = XK_Control_L, + [KEY_A] = XK_a, + [KEY_S] = XK_s, + [KEY_D] = XK_d, + [KEY_F] = XK_f, + [KEY_G] = XK_g, + [KEY_H] = XK_h, + [KEY_J] = XK_j, + [KEY_K] = XK_k, + [KEY_L] = XK_l, + [KEY_SEMICOLON] = XK_semicolon, + [KEY_APOSTROPHE] = XK_apostrophe, + [KEY_GRAVE] = XK_grave, + [KEY_LEFTSHIFT] = XK_Shift_L, + [KEY_BACKSLASH] = XK_backslash, + [KEY_Z] = XK_z, + [KEY_X] = XK_x, + [KEY_C] = XK_c, + [KEY_V] = XK_v, + [KEY_B] = XK_b, + [KEY_N] = XK_n, + [KEY_M] = XK_m, + [KEY_COMMA] = XK_comma, + [KEY_DOT] = XK_period, + [KEY_SLASH] = XK_slash, + [KEY_RIGHTSHIFT] = XK_Shift_R, + [KEY_KPASTERISK] = XK_KP_Multiply, + [KEY_LEFTALT] = XK_Alt_L, + [KEY_SPACE] = XK_space, + [KEY_CAPSLOCK] = XK_Caps_Lock, + [KEY_F1] = XK_F1, + [KEY_F2] = XK_F2, + [KEY_F3] = XK_F3, + [KEY_F4] = XK_F4, + [KEY_F5] = XK_F5, + [KEY_F6] = XK_F6, + [KEY_F7] = XK_F7, + [KEY_F8] = XK_F8, + [KEY_F9] = XK_F9, + [KEY_F10] = XK_F10, + [KEY_NUMLOCK] = XK_Num_Lock, + [KEY_SCROLLLOCK] = XK_Scroll_Lock, + [KEY_KP7] = XK_KP_Home, + [KEY_KP8] = XK_KP_Up, + [KEY_KP9] = XK_KP_Page_Up, + [KEY_KPMINUS] = XK_KP_Subtract, + [KEY_KP4] = XK_KP_Left, + [KEY_KP5] = XK_KP_Begin, + [KEY_KP6] = XK_KP_Right, + [KEY_KPPLUS] = XK_KP_Add, + [KEY_KP1] = XK_KP_End, + [KEY_KP2] = XK_KP_Down, + [KEY_KP3] = XK_KP_Page_Down, + [KEY_KP0] = XK_KP_Insert, + [KEY_KPDOT] = XK_KP_Delete, + [KEY_F11] = XK_F11, + [KEY_F12] = XK_F12, + [KEY_KPENTER] = XK_KP_Enter, + [KEY_RIGHTCTRL] = XK_Control_R, + [KEY_KPSLASH] = XK_KP_Divide, + [KEY_RIGHTALT] = XK_Alt_R, + [KEY_LINEFEED] = XK_Linefeed, + [KEY_HOME] = XK_Home, + [KEY_UP] = XK_Up, + [KEY_PAGEUP] = XK_Page_Up, + [KEY_LEFT] = XK_Left, + [KEY_RIGHT] = XK_Right, + [KEY_END] = XK_End, + [KEY_DOWN] = XK_Down, + [KEY_PAGEDOWN] = XK_Page_Down, + [KEY_INSERT] = XK_Insert, + [KEY_DELETE] = XK_Delete, + [KEY_KPEQUAL] = XK_KP_Equal, + [KEY_LEFTMETA] = XK_Meta_L, + [KEY_RIGHTMETA] = XK_Meta_R, +}; +#define KEYTAB_SIZE (KEY_RIGHTMETA + 1) +_Static_assert( + (KEYTAB_SIZE == sizeof(keytab_normal) / sizeof(*keytab_normal)), + "The KEYTAB_SIZE #define is incorrect!" +); + +static const uint32_t keytab_numlock[KEYTAB_SIZE] = { + [KEY_KP7] = XK_KP_7, + [KEY_KP8] = XK_KP_8, + [KEY_KP9] = XK_KP_9, + [KEY_KP4] = XK_KP_4, + [KEY_KP5] = XK_KP_5, + [KEY_KP6] = XK_KP_6, + [KEY_KP1] = XK_KP_1, + [KEY_KP2] = XK_KP_2, + [KEY_KP3] = XK_KP_3, + [KEY_KP0] = XK_KP_0, +}; + +static const uint32_t keytab_shift[KEYTAB_SIZE] = { + [KEY_1] = XK_exclam, + [KEY_2] = XK_at, + [KEY_3] = XK_numbersign, + [KEY_4] = XK_dollar, + [KEY_5] = XK_percent, + [KEY_6] = XK_asciicircum, + [KEY_7] = XK_ampersand, + [KEY_8] = XK_asterisk, + [KEY_9] = XK_parenleft, + [KEY_0] = XK_parenright, + [KEY_MINUS] = XK_underscore, + [KEY_EQUAL] = XK_plus, + [KEY_Q] = XK_Q, + [KEY_W] = XK_W, + [KEY_E] = XK_E, + [KEY_R] = XK_R, + [KEY_T] = XK_T, + [KEY_Y] = XK_Y, + [KEY_U] = XK_U, + [KEY_I] = XK_I, + [KEY_O] = XK_O, + [KEY_P] = XK_P, + [KEY_LEFTBRACE] = XK_braceleft, + [KEY_RIGHTBRACE] = XK_braceright, + [KEY_A] = XK_A, + [KEY_S] = XK_S, + [KEY_D] = XK_D, + [KEY_F] = XK_F, + [KEY_G] = XK_G, + [KEY_H] = XK_H, + [KEY_J] = XK_J, + [KEY_K] = XK_K, + [KEY_L] = XK_L, + [KEY_SEMICOLON] = XK_colon, + [KEY_APOSTROPHE] = XK_quotedbl, + [KEY_GRAVE] = XK_asciitilde, + [KEY_BACKSLASH] = XK_bar, + [KEY_Z] = XK_Z, + [KEY_X] = XK_X, + [KEY_C] = XK_C, + [KEY_V] = XK_V, + [KEY_B] = XK_B, + [KEY_N] = XK_N, + [KEY_M] = XK_M, + [KEY_COMMA] = XK_less, + [KEY_DOT] = XK_greater, + [KEY_SLASH] = XK_question, +}; + +static const uint32_t keytab_capslock[KEYTAB_SIZE] = { + [KEY_Q] = XK_Q, + [KEY_W] = XK_W, + [KEY_E] = XK_E, + [KEY_R] = XK_R, + [KEY_T] = XK_T, + [KEY_Y] = XK_Y, + [KEY_U] = XK_U, + [KEY_I] = XK_I, + [KEY_O] = XK_O, + [KEY_P] = XK_P, + [KEY_A] = XK_A, + [KEY_S] = XK_S, + [KEY_D] = XK_D, + [KEY_F] = XK_F, + [KEY_G] = XK_G, + [KEY_H] = XK_H, + [KEY_J] = XK_J, + [KEY_K] = XK_K, + [KEY_L] = XK_L, + [KEY_Z] = XK_Z, + [KEY_X] = XK_X, + [KEY_C] = XK_C, + [KEY_V] = XK_V, + [KEY_B] = XK_B, + [KEY_N] = XK_N, + [KEY_M] = XK_M, +}; + +static const struct { + enum kmscon_modifier mod; + enum { + MOD_NORMAL = 1, + MOD_LOCK, + } type; +} modmap[KEYTAB_SIZE] = { + [KEY_LEFTCTRL] = { KMSCON_CONTROL_MASK, MOD_NORMAL }, + [KEY_LEFTSHIFT] = { KMSCON_SHIFT_MASK, MOD_NORMAL }, + [KEY_RIGHTSHIFT] = { KMSCON_SHIFT_MASK, MOD_NORMAL }, + [KEY_LEFTALT] = { KMSCON_MOD1_MASK, MOD_NORMAL }, + [KEY_CAPSLOCK] = { KMSCON_LOCK_MASK, MOD_LOCK }, + [KEY_NUMLOCK] = { KMSCON_MOD2_MASK, MOD_LOCK }, + [KEY_RIGHTCTRL] = { KMSCON_CONTROL_MASK, MOD_NORMAL }, + [KEY_RIGHTALT] = { KMSCON_MOD1_MASK, MOD_NORMAL }, + [KEY_LEFTMETA] = { KMSCON_MOD4_MASK, MOD_NORMAL }, + [KEY_RIGHTMETA] = { KMSCON_MOD4_MASK, MOD_NORMAL }, +}; + +int kmscon_kbd_new(struct kmscon_kbd **out, struct kmscon_kbd_desc *desc) +{ + struct kmscon_kbd *kbd; + + kbd = malloc(sizeof(*kbd)); + if (!kbd) + return -ENOMEM; + + memset(kbd, 0, sizeof(*kbd)); + + kbd->desc = desc; + kmscon_kbd_desc_ref(desc); + + *out = kbd; + return 0; +} + +void kmscon_kbd_ref(struct kmscon_kbd *kbd) +{ + if (!kbd) + return; + + ++kbd->ref; +} + +void kmscon_kbd_unref(struct kmscon_kbd *kbd) +{ + if (!kbd || !kbd->ref) + return; + + if (--kbd->ref) + return; + + kmscon_kbd_desc_unref(kbd->desc); + free(kbd); +} + +void kmscon_kbd_reset(struct kmscon_kbd *kbd, int evdev_fd) +{ + int i; + /* One long should be enough (LED_MAX is currently 16). */ + unsigned long leds, bit; + + if (!kbd) + return; + + kbd->mods = 0; + + errno = 0; + ioctl(evdev_fd, EVIOCGLED(sizeof(leds)), &leds); + if (errno) { + log_warning("kbd-dumb: cannot 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)) + continue; + + switch (i) { + case LED_NUML: + kbd->mods |= KMSCON_MOD2_MASK; + break; + case LED_CAPSL: + kbd->mods |= KMSCON_LOCK_MASK; + break; + } + } +} + +int kmscon_kbd_process_key(struct kmscon_kbd *kbd, + enum kmscon_key_state key_state, + uint16_t code, + struct kmscon_input_event *out) +{ + uint32_t keysym; + enum kmscon_modifier mod; + int mod_type; + + /* Ignore unknown keycodes. */ + if (code >= KEYTAB_SIZE) + return -ENOKEY; + + if (modmap[code].mod) { + mod = modmap[code].mod; + mod_type = modmap[code].type; + + /* + * We release locked modifiers on key press, like the kernel, + * but unlike XKB. + */ + if (key_state == KMSCON_KEY_PRESSED) { + if (mod_type == MOD_NORMAL) + kbd->mods |= mod; + else if (mod_type == MOD_LOCK) + kbd->mods ^= mod; + } else if (key_state == KMSCON_KEY_RELEASED) { + if (mod_type == MOD_NORMAL) + kbd->mods &= ~mod; + } + + /* Don't deliver events purely for modifiers. */ + return -ENOKEY; + } + + if (key_state == KMSCON_KEY_RELEASED) + return -ENOKEY; + + keysym = 0; + + if (!keysym && kbd->mods & KMSCON_MOD2_MASK) + keysym = keytab_numlock[code]; + if (!keysym && kbd->mods & KMSCON_SHIFT_MASK) + keysym = keytab_shift[code]; + if (!keysym && kbd->mods & KMSCON_LOCK_MASK) + keysym = keytab_capslock[code]; + if (!keysym) + keysym = keytab_normal[code]; + + if (!keysym) + return -ENOKEY; + + out->keycode = code; + out->keysym = keysym; + out->unicode = KeysymToUcs4(keysym) ?: KMSCON_INPUT_INVALID; + out->mods = kbd->mods; + + return 0; +} + +int kmscon_kbd_desc_new(struct kmscon_kbd_desc **out, const char *layout, + const char *variant, const char *options) +{ + struct kmscon_kbd_desc *desc; + + if (!out) + return -EINVAL; + + log_debug("kbd-dumb: new keyboard description (%s, %s, %s)\n", + layout, variant, options); + + desc = malloc(sizeof(*desc)); + if (!desc) + return -ENOMEM; + + memset(desc, 0, sizeof(*desc)); + + desc->ref = 1; + *out = desc; + return 0; +} + +void kmscon_kbd_desc_ref(struct kmscon_kbd_desc *desc) +{ + if (!desc) + return; + + ++desc->ref; +} + +void kmscon_kbd_desc_unref(struct kmscon_kbd_desc *desc) +{ + if (!desc || !desc->ref) + return; + + if (--desc->ref) + return; + + log_debug("kbd-dumb: destroying keyboard description\n"); + + free(desc); +} + +void kmscon_kbd_keysym_to_string(uint32_t keysym, char *str, size_t size) +{ + snprintf(str, size, "%#x", keysym); +} diff --git a/src/input_xkb.c b/src/kbd_xkb.c similarity index 87% rename from src/input_xkb.c rename to src/kbd_xkb.c index bf2c609..b46574c 100644 --- a/src/input_xkb.c +++ b/src/kbd_xkb.c @@ -64,456 +64,60 @@ #include #include #include +#include -#include "input_xkb.h" +#include "kbd.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 void init_autorepeat(struct xkb_desc *desc); -static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode); -static int init_compat_for_keysym(struct xkb_desc *desc, KeyCode keycode, - uint8_t group, uint16_t level); -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); +struct kmscon_kbd_desc { + unsigned long ref; -static bool process_action(struct xkb_desc *desc, struct xkb_state *state, - KeyCode keycode, enum kmscon_key_state key_state, - union xkb_action *action); -static bool process_mod_action(struct xkb_desc *desc, struct xkb_state *state, - KeyCode keycode, enum kmscon_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 kmscon_key_state key_state, - struct xkb_group_action *action); - -static bool should_key_repeat(struct xkb_desc *desc, KeyCode keycode); -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 kmscon_xkb_new_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, - }; +struct kmscon_kbd { + unsigned long ref; + struct kmscon_kbd_desc *desc; - desc = xkb_compile_keymap_from_rules(&rmlvo); - if (!desc) - return -EFAULT; + struct xkb_state state; +}; - /* The order of these is important! */ - init_compat(desc); - init_key_types(desc); - init_actions(desc); - init_indicators(desc); - init_autorepeat(desc); +int kmscon_kbd_new(struct kmscon_kbd **out, struct kmscon_kbd_desc *desc) +{ + struct kmscon_kbd *kbd; - *out = desc; + kbd = malloc(sizeof(*kbd)); + if (!kbd) + return -ENOMEM; + + memset(kbd, 0, sizeof(*kbd)); + + kbd->desc = desc; + kmscon_kbd_desc_ref(desc); + + *out = kbd; return 0; } -void kmscon_xkb_free_desc(struct xkb_desc *desc) +void kmscon_kbd_ref(struct kmscon_kbd *kbd) { - if (!desc) + if (!kbd) 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); + ++kbd->ref; } -/* - * 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) +void kmscon_kbd_unref(struct kmscon_kbd *kbd) { - /* If we use KeyCode it overflows. */ - unsigned int keycode; + if (!kbd || !kbd->ref) + return; - for (keycode = desc->min_key_code; keycode <= desc->max_key_code; keycode++) - init_compat_for_keycode(desc, keycode); -} + if (--kbd->ref) + return; -static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode) -{ - int ret; - int i, bit; - - uint8_t group; - uint16_t level; - int num_groups; - int num_levels; - - /* - * 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; - - num_groups = XkbKeyNumGroups(desc, keycode); - - /* - * We need to track the sym level in order to support LevelOneOnly, - * which is used in some symbol interpretations. - */ - - for (group=0, i=0; group < num_groups; group++) { - num_levels = XkbKeyGroupWidth(desc, keycode, group); - - for (level=0; level < num_levels; level++) { - ret = init_compat_for_keysym(desc, keycode, - group, level); - if (ret) - return ret; - } - } - - /* - * 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; -} - -static int init_compat_for_keysym(struct xkb_desc *desc, KeyCode keycode, - uint8_t group, uint16_t level) -{ - int ret; - uint8_t key_modmap; - uint32_t sym; - struct xkb_sym_interpret *si; - union xkb_action *action; - - key_modmap = desc->map->modmap[keycode]; - sym = XkbKeySymEntry(desc, keycode, level, group); - si = find_sym_interpret(desc, sym, level, key_modmap); - - if (!si) - return 0; - - /* Set the key action mapping. */ - if (si->act.type != XkbSA_NoAction) { - ret = allocate_key_acts(desc, keycode); - if (ret) - return ret; - - 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; - - 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. - * Note: [xserver] and [Lib] do different things here, and it - * doesn't seem to matter much. So it's commented for now. - */ - /* 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); - } -} - -/* - * We don't do soft repeat currently, but we use the controls to filter out - * which evdev repeats to send. - */ -static void init_autorepeat(struct xkb_desc *desc) -{ - /* - * This is taken from /include/site.h - * If a bit is off for a keycode, it should not repeat. - */ - static const char DEFAULT_AUTOREPEATS[XkbPerKeyBitArraySize] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - memcpy(desc->ctrls->per_key_repeat, - DEFAULT_AUTOREPEATS, XkbPerKeyBitArraySize); - - desc->ctrls->enabled_ctrls |= XkbRepeatKeysMask; -} - -/* Whether to send out a repeat event for the key. */ -static bool should_key_repeat(struct xkb_desc *desc, KeyCode keycode) -{ - unsigned const char *pkr; - - /* Repeats globally disabled. */ - if (!(desc->ctrls->enabled_ctrls & XkbRepeatKeysMask)) - return false; - - /* Repeats disabled for the specific key. */ - pkr = desc->ctrls->per_key_repeat; - if (!(pkr[keycode / 8] & (0x01 << (keycode % 8)))) - return false; - - /* Don't repeat modifiers. */ - if (desc->map->modmap[keycode] != 0) - return false; - - return true; + kmscon_kbd_desc_unref(kbd->desc); + free(kbd); } static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods) @@ -542,367 +146,6 @@ static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc, 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 kmscon_xkb_reset_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)) - 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. - */ -static 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 match 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 kmscon_xkb_process_evdev_key(struct xkb_desc *desc, - struct xkb_state *state, - enum kmscon_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, event_filled; - - keycode = code + desc->min_key_code; - - /* Valid keycode. */ - if (!XkbKeycodeInRange(desc, keycode)) - return false; - /* Active keycode. */ - if (XkbKeyNumSyms(desc, keycode) == 0) - return false; - /* Unwanted repeat. */ - if (key_state == KMSCON_KEY_REPEATED && - !should_key_repeat(desc, keycode)) - 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 != KMSCON_KEY_REPEATED) { - action = XkbKeyActionEntry(desc, keycode, shift_level, group); - state_changed = process_action(desc, state, keycode, - key_state, action); - } - - event_filled = false; - if (key_state != KMSCON_KEY_RELEASED) { - out->keycode = code; - out->keysym = sym; - /* 1-to-1 match - this might change. */ - out->mods = state->mods; - out->unicode = KeysymToUcs4(sym); - - if (out->unicode == 0) - out->unicode = KMSCON_INPUT_INVALID; - - event_filled = 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 event_filled; -} - -/* - * 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 kmscon_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 kmscon_key_state key_state, - struct xkb_mod_action *action) -{ - uint8_t mods; - uint8_t saved_mods; - uint8_t flags = action->flags; - - if (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 == KMSCON_KEY_PRESSED) { - state->base_mods |= mods; - } else if (key_state == KMSCON_KEY_RELEASED) { - state->base_mods &= ~mods; - if (flags & XkbSA_ClearLocks) - state->locked_mods &= ~mods; - } - - break; - case XkbSA_LatchMods: - if (key_state == KMSCON_KEY_PRESSED) { - state->base_mods |= mods; - } else if (key_state == KMSCON_KEY_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 == KMSCON_KEY_PRESSED) { - state->base_mods |= mods; - state->locked_mods ^= mods; - } else if (key_state == KMSCON_KEY_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 kmscon_key_state key_state, - struct xkb_group_action *action) -{ - int16_t group = action->group; - uint8_t flags = action->flags; - - /* - * action->group is signed and may be negative if GroupAbsolute - * is not set. A group itself cannot be negative and is unsigend. - * Therefore we extend these to int16 to avoid underflow and - * signedness issues. Be careful! - */ - int16_t base_group = state->base_group; - int16_t latched_group = state->latched_group; - int16_t locked_group = state->locked_group; - - /* - * 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 == KMSCON_KEY_PRESSED) { - if (flags & XkbSA_GroupAbsolute) - base_group = group; - else - base_group += group; - } else if (key_state == KMSCON_KEY_RELEASED) { - if (flags & XkbSA_ClearLocks) - locked_group = 0; - } - - break; - case XkbSA_LatchGroup: - if (key_state == KMSCON_KEY_PRESSED) { - if (flags & XkbSA_GroupAbsolute) - base_group = group; - else - base_group += group; - } else if (key_state == KMSCON_KEY_RELEASED) { - if ((flags & XkbSA_LatchToLock) && latched_group) { - locked_group += group; - latched_group -= group; - } else { - latched_group += group; - } - } - - break; - case XkbSA_LockGroup: - if (key_state == KMSCON_KEY_PRESSED) { - if (flags & XkbSA_GroupAbsolute) - locked_group = group; - else - locked_group += group; - } - - break; - } - - /* Bring what was changed back into range. */ - state->base_group = wrap_group_control(desc, base_group); - state->locked_group = wrap_group_control(desc, locked_group); - state->latched_group = wrap_group_control(desc, latched_group); - update_effective_group(desc, state); - return true; -} - /* * Helper function for the wrap_group_* functions. * See [Lib] 11.7.1 for the rules. @@ -999,3 +242,811 @@ static void update_effective_group(struct xkb_desc *desc, group = state->base_group + state->locked_group + state->latched_group; state->group = wrap_group_control(desc, group); } + +/* + * 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 kmscon_key_state key_state, + struct xkb_group_action *action) +{ + int16_t group = action->group; + uint8_t flags = action->flags; + + /* + * action->group is signed and may be negative if GroupAbsolute + * is not set. A group itself cannot be negative and is unsigend. + * Therefore we extend these to int16 to avoid underflow and + * signedness issues. Be careful! + */ + int16_t base_group = state->base_group; + int16_t latched_group = state->latched_group; + int16_t locked_group = state->locked_group; + + /* + * 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 == KMSCON_KEY_PRESSED) { + if (flags & XkbSA_GroupAbsolute) + base_group = group; + else + base_group += group; + } else if (key_state == KMSCON_KEY_RELEASED) { + if (flags & XkbSA_ClearLocks) + locked_group = 0; + } + + break; + case XkbSA_LatchGroup: + if (key_state == KMSCON_KEY_PRESSED) { + if (flags & XkbSA_GroupAbsolute) + base_group = group; + else + base_group += group; + } else if (key_state == KMSCON_KEY_RELEASED) { + if ((flags & XkbSA_LatchToLock) && latched_group) { + locked_group += group; + latched_group -= group; + } else { + latched_group += group; + } + } + + break; + case XkbSA_LockGroup: + if (key_state == KMSCON_KEY_PRESSED) { + if (flags & XkbSA_GroupAbsolute) + locked_group = group; + else + locked_group += group; + } + + break; + } + + /* Bring what was changed back into range. */ + state->base_group = wrap_group_control(desc, base_group); + state->locked_group = wrap_group_control(desc, locked_group); + state->latched_group = wrap_group_control(desc, latched_group); + update_effective_group(desc, state); + return true; +} + +/* + * 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 kmscon_key_state key_state, + struct xkb_mod_action *action) +{ + uint8_t mods; + uint8_t saved_mods; + uint8_t flags = action->flags; + + if (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 == KMSCON_KEY_PRESSED) { + state->base_mods |= mods; + } else if (key_state == KMSCON_KEY_RELEASED) { + state->base_mods &= ~mods; + if (flags & XkbSA_ClearLocks) + state->locked_mods &= ~mods; + } + + break; + case XkbSA_LatchMods: + if (key_state == KMSCON_KEY_PRESSED) { + state->base_mods |= mods; + } else if (key_state == KMSCON_KEY_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 == KMSCON_KEY_PRESSED) { + state->base_mods |= mods; + state->locked_mods ^= mods; + } else if (key_state == KMSCON_KEY_RELEASED) { + state->base_mods &= ~mods; + } + + break; + } + + update_effective_mods(desc, state); + return true; +} + +/* + * 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 kmscon_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; +} + +/* + * 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. + */ +static 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 match 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; +} + +/* Whether to send out a repeat event for the key. */ +static bool should_key_repeat(struct xkb_desc *desc, KeyCode keycode) +{ + unsigned const char *pkr; + + /* Repeats globally disabled. */ + if (!(desc->ctrls->enabled_ctrls & XkbRepeatKeysMask)) + return false; + + /* Repeats disabled for the specific key. */ + pkr = desc->ctrls->per_key_repeat; + if (!(pkr[keycode / 8] & (0x01 << (keycode % 8)))) + return false; + + /* Don't repeat modifiers. */ + if (desc->map->modmap[keycode] != 0) + return false; + + return true; +} + +int kmscon_kbd_process_key(struct kmscon_kbd *kbd, + enum kmscon_key_state key_state, + uint16_t code, + struct kmscon_input_event *out) +{ + struct xkb_desc *desc; + struct xkb_state *state; + KeyCode keycode; + uint8_t group; + uint16_t shift_level; + uint32_t sym; + union xkb_action *action; + bool state_changed, event_filled; + + if (!kbd) + return -EINVAL; + + desc = kbd->desc->desc; + state = &kbd->state; + + keycode = code + desc->min_key_code; + + /* Valid keycode. */ + if (!XkbKeycodeInRange(desc, keycode)) + return -ENOKEY; + /* Active keycode. */ + if (XkbKeyNumSyms(desc, keycode) == 0) + return -ENOKEY; + /* Unwanted repeat. */ + if (key_state == KMSCON_KEY_REPEATED && + !should_key_repeat(desc, keycode)) + return -ENOKEY; + + 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 != KMSCON_KEY_REPEATED) { + action = XkbKeyActionEntry(desc, keycode, shift_level, group); + state_changed = process_action(desc, state, keycode, + key_state, action); + } + + event_filled = false; + if (key_state != KMSCON_KEY_RELEASED) { + out->keycode = code; + out->keysym = sym; + /* 1-to-1 match - this might change. */ + out->mods = state->mods; + out->unicode = KeysymToUcs4(sym); + + if (out->unicode == 0) + out->unicode = KMSCON_INPUT_INVALID; + + event_filled = 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 event_filled ? 0 : -ENOKEY; +} + +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; +} + +/* + * 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 kmscon_kbd_reset(struct kmscon_kbd *kbd, int evdev_fd) +{ + int i; + struct xkb_desc *desc; + struct xkb_state *state; + /* One long should be enough (LED_MAX is currently 16). */ + unsigned long leds, bit; + struct xkb_indicator_map *im; + + if (!kbd) + return; + + desc = kbd->desc->desc; + state = &kbd->state; + + 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("kbd-xkb: cannot 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)) + 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); +} + +/* + * We don't do soft repeat currently, but we use the controls to filter out + * which evdev repeats to send. + */ +static void init_autorepeat(struct xkb_desc *desc) +{ + /* + * This is taken from /include/site.h + * If a bit is off for a keycode, it should not repeat. + */ + static const char DEFAULT_AUTOREPEATS[XkbPerKeyBitArraySize] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + memcpy(desc->ctrls->per_key_repeat, + DEFAULT_AUTOREPEATS, XkbPerKeyBitArraySize); + + desc->ctrls->enabled_ctrls |= XkbRepeatKeysMask; +} + +/* + * 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 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 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); + } +} + +/* + * 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; + } + } +} + +/* + * 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; +} + +/* + * 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. + * Note: [xserver] and [Lib] do different things here, and it + * doesn't seem to matter much. So it's commented for now. + */ + /* 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; +} + +/* + * 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; +} + +static int init_compat_for_keysym(struct xkb_desc *desc, KeyCode keycode, + uint8_t group, uint16_t level) +{ + int ret; + uint8_t key_modmap; + uint32_t sym; + struct xkb_sym_interpret *si; + union xkb_action *action; + + key_modmap = desc->map->modmap[keycode]; + sym = XkbKeySymEntry(desc, keycode, level, group); + si = find_sym_interpret(desc, sym, level, key_modmap); + + if (!si) + return 0; + + /* Set the key action mapping. */ + if (si->act.type != XkbSA_NoAction) { + ret = allocate_key_acts(desc, keycode); + if (ret) + return ret; + + 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; + + return 0; +} + +static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode) +{ + int ret; + int i, bit; + + uint8_t group; + uint16_t level; + int num_groups; + int num_levels; + + /* + * 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; + + num_groups = XkbKeyNumGroups(desc, keycode); + + /* + * We need to track the sym level in order to support LevelOneOnly, + * which is used in some symbol interpretations. + */ + + for (group=0, i=0; group < num_groups; group++) { + num_levels = XkbKeyGroupWidth(desc, keycode, group); + + for (level=0; level < num_levels; level++) { + ret = init_compat_for_keysym(desc, keycode, + group, level); + if (ret) + return ret; + } + } + + /* + * 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; +} + +/* + * 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); +} + +/* + * Create a ready-to-use xkb description object. It is used in most places + * having to do with XKB. + */ +int kmscon_kbd_desc_new(struct kmscon_kbd_desc **out, const char *layout, + const char *variant, const char *options) +{ + struct kmscon_kbd_desc *desc; + + if (!out) + return -EINVAL; + + log_debug("kbd-xkb: new keyboard description (%s, %s, %s)\n", + layout, variant, options); + + desc = malloc(sizeof(*desc)); + if (!desc) + return -ENOMEM; + + memset(desc, 0, sizeof(*desc)); + desc->ref = 1; + + struct xkb_rule_names rmlvo = { + .rules = "evdev", + .model = "evdev", + .layout = layout, + .variant = variant, + .options = options, + }; + + desc->desc = xkb_compile_keymap_from_rules(&rmlvo); + if (!desc->desc) { + log_err("kbd-xkb: cannot compile keymap from rules\n"); + free(desc); + return -EFAULT; + } + + /* The order of these is important! */ + init_compat(desc->desc); + init_key_types(desc->desc); + init_actions(desc->desc); + init_indicators(desc->desc); + init_autorepeat(desc->desc); + + *out = desc; + return 0; +} + +void kmscon_kbd_desc_ref(struct kmscon_kbd_desc *desc) +{ + if (!desc) + return; + + ++desc->ref; +} + +void kmscon_kbd_desc_unref(struct kmscon_kbd_desc *desc) +{ + if (!desc || !desc->ref) + return; + + if (--desc->ref) + return; + + log_debug("kbd-xkb: destroying keyboard description\n"); + + /* + * 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->desc); + free(desc); +} + +void kmscon_kbd_keysym_to_string(uint32_t keysym, char *str, size_t size) +{ + xkb_keysym_to_string(keysym, str, size); +} diff --git a/tests/test_input.c b/tests/test_input.c index 0d349cc..a4500b2 100644 --- a/tests/test_input.c +++ b/tests/test_input.c @@ -32,13 +32,12 @@ #include #include #include -#include -#include #include #include "eloop.h" #include "input.h" +#include "kbd.h" #include "log.h" static bool terminate; @@ -86,27 +85,17 @@ static void print_modifiers(unsigned int mods) 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 == KMSCON_INPUT_INVALID) { - xkb_keysym_to_string(ev->keysym, s, sizeof(s)); + kmscon_kbd_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); + printf("unicode %lc ", ev->unicode); } print_modifiers(ev->mods); } @@ -118,7 +107,7 @@ int main(int argc, char **argv) struct kmscon_input *input; struct kmscon_signal *sigint, *sigquit; - if (!setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setlocale(LC_ALL, "")) { log_err("Cannot set locale: %m\n"); ret = -EFAULT; goto err_out;