From ff7fa68e2d539a110e3f16a4df2158d4b4ce2e77 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 17 Jan 2012 23:12:44 +0200 Subject: [PATCH 1/5] Add skeleton for multiple keyboard handling backends This defines the API the keyboard backends will need to implement. It is based on what's in input_xkb.h but cleaned up and simplified. Signed-off-by: Ran Benita --- src/kbd.h | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/kbd.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 */ From 73828a26fc6fd2c93eb24a7407bd81fa4dadca9c Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 17 Jan 2012 23:57:11 +0200 Subject: [PATCH 2/5] input, xkb: port to new kbd object This commit ports the XKB handling to the new kbd interface, and makes the input subsystem use it without any direct knowledge of XKB. Some code is moved around but there are no functional changes. Signed-off-by: Ran Benita --- Makefile.am | 10 +- configure.ac | 15 +- src/input.c | 48 +- src/input.h | 11 - src/input_xkb.h | 48 - src/{input_xkb.c => kbd_xkb.c} | 1631 ++++++++++++++++---------------- 6 files changed, 892 insertions(+), 871 deletions(-) delete mode 100644 src/input_xkb.h rename src/{input_xkb.c => kbd_xkb.c} (87%) diff --git a/Makefile.am b/Makefile.am index 21ca61e..74654be 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,11 +28,17 @@ 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 +# This is temporary +endif + if USE_PANGO libkmscon_core_la_SOURCES += \ src/font_pango.c src/font.h diff --git a/configure.ac b/configure.ac index d4db1e3..f9e6e07 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,8 @@ PKG_CHECK_MODULES([UDEV], [libudev]) AC_SUBST(UDEV_CFLAGS) AC_SUBST(UDEV_LIBS) -PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon]) +PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon], + [have_xkbcommon=yes], [have_xkbcommon=no]) AC_SUBST(XKBCOMMON_CFLAGS) AC_SUBST(XKBCOMMON_LIBS) @@ -58,6 +59,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/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); +} From fc53f0f2985eb4968873c962a2316c06c27bddea Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 18 Jan 2012 00:03:16 +0200 Subject: [PATCH 3/5] build: depend on xproto explicitly Currently it's required by xkbcommon. However, we will need it for other backends as well, even with xkbcommon support disabled. xproto consisted only of static header files. We use the XK_ keysym definitions. It is therefore only required during compilation and we shouldn't needlessly duplicate it ourselves. Signed-off-by: Ran Benita --- Makefile.am | 2 ++ README | 1 + configure.ac | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/Makefile.am b/Makefile.am index 74654be..fa4be3a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,6 +56,7 @@ libkmscon_core_la_CPPFLAGS = \ $(PANGO_CFLAGS) \ $(UDEV_CFLAGS) \ $(FREETYPE2_CFLAGS) \ + $(XPROTO_CFLAGS) \ $(XKBCOMMON_CFLAGS) \ $(GLIB_CFLAGS) libkmscon_core_la_LIBADD = \ @@ -66,6 +67,7 @@ libkmscon_core_la_LIBADD = \ $(PANGO_LIBS) \ $(UDEV_LIBS) \ $(FREETYPE2_LIBS) \ + $(XPROTO_LIBS) \ $(XKBCOMMON_LIBS) \ $(GLIB_LIBS) diff --git a/README b/README index 375888f..a6e3919 100644 --- a/README +++ b/README @@ -9,6 +9,7 @@ console. - mesa: providing an OpenGL implementation (must be compiled with EGL, gbm and GL libraries) - udev: providing input device hotplug + - xproto (build time dependency): definition of key symbols - libxkbcommon: keyboard handling - glib: only for Unicode handling - One of: diff --git a/configure.ac b/configure.ac index f9e6e07..3d14c33 100644 --- a/configure.ac +++ b/configure.ac @@ -41,6 +41,10 @@ PKG_CHECK_MODULES([UDEV], [libudev]) AC_SUBST(UDEV_CFLAGS) AC_SUBST(UDEV_LIBS) +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) From 37cbd7d962b96dee7bb45b91b9a3b369b8d4143d Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 18 Jan 2012 00:09:16 +0200 Subject: [PATCH 4/5] kbd: add a new "dumb" keyboard backend This commit adds a very simple keyboard backend which does basic keycode interpretation. It is used as a fallback when xkbcommon is unavailable or is not required. See the file header for what is supported. Signed-off-by: Ran Benita --- Makefile.am | 4 +- README | 3 +- src/kbd_dumb.c | 472 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 477 insertions(+), 2 deletions(-) create mode 100644 src/kbd_dumb.c diff --git a/Makefile.am b/Makefile.am index fa4be3a..63987f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,9 @@ libkmscon_core_la_SOURCES += \ src/kbd_xkb.c src/kbd.h \ external/imKStoUCS.c external\imKStoUCS.h else -# This is temporary +libkmscon_core_la_SOURCES += \ + src/kbd_dumb.c src/kbd.h \ + external/imKStoUCS.c external\imKStoUCS.h endif if USE_PANGO diff --git a/README b/README index a6e3919..9c57407 100644 --- a/README +++ b/README @@ -10,7 +10,8 @@ console. and GL libraries) - udev: providing input device hotplug - xproto (build time dependency): definition of key symbols - - libxkbcommon: keyboard handling + - 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/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); +} From 84c3fe7ba6dd38a127bd7002f0a348f0193951fd Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 18 Jan 2012 00:14:16 +0200 Subject: [PATCH 5/5] test_input: simplify and drop xkbcommon dependency Signed-off-by: Ran Benita --- Makefile.am | 7 +------ tests/test_input.c | 19 ++++--------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index 63987f0..02ac51f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -102,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/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;