/* * uterm - Linux User-Space Terminal * * Copyright (c) 2011 Ran Benita * Copyright (c) 2012-2013 David Herrmann * * 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 #include #include "shl_hook.h" #include "shl_llog.h" #include "shl_misc.h" #include "uterm_input.h" #include "uterm_input_internal.h" #define LLOG_SUBSYSTEM "uterm_uxkb" extern const char _binary_src_uterm_input_fallback_xkb_bin_start[]; extern const char _binary_src_uterm_input_fallback_xkb_bin_end[]; static void uxkb_log(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args) { struct uterm_input *input; unsigned int sev; input = xkb_context_get_user_data(context); if (!input->llog) return; switch (level) { case XKB_LOG_LEVEL_CRITICAL: sev = LLOG_CRITICAL; break; case XKB_LOG_LEVEL_ERROR: sev = LLOG_ERROR; break; case XKB_LOG_LEVEL_WARNING: sev = LLOG_WARNING; break; case XKB_LOG_LEVEL_INFO: sev = LLOG_INFO; break; case XKB_LOG_LEVEL_DEBUG: /* fallthrough */ default: sev = LLOG_DEBUG; break; } input->llog(input->llog_data, LLOG_DEFAULT, sev, format, args); } int uxkb_desc_init(struct uterm_input *input, const char *model, const char *layout, const char *variant, const char *options, const char *keymap) { int ret; struct xkb_rule_names rmlvo = { .rules = "evdev", .model = model, .layout = layout, .variant = variant, .options = options, }; const char *fallback; fallback = _binary_src_uterm_input_fallback_xkb_bin_start; input->ctx = xkb_context_new(0); if (!input->ctx) { llog_error(input, "cannot create XKB context"); return -ENOMEM; } /* Set logging function. You can use XKB_LOG_VERBOSITY and XKB_LOG_LEVEL * to change the xkbcommon logger. That's why we don't touch the * verbosity and level here. */ xkb_context_set_user_data(input->ctx, input); xkb_context_set_log_fn(input->ctx, uxkb_log); /* If a complete keymap file was given, first try that. */ if (keymap && *keymap) { input->keymap = xkb_keymap_new_from_string(input->ctx, keymap, XKB_KEYMAP_FORMAT_TEXT_V1, 0); if (input->keymap) { llog_debug(input, "new keyboard description from memory"); return 0; } llog_warn(input, "cannot parse keymap, reverting to rmlvo"); } input->keymap = xkb_keymap_new_from_names(input->ctx, &rmlvo, 0); if (!input->keymap) { llog_warn(input, "failed to create keymap (%s, %s, %s, %s), " "reverting to default system keymap", model, layout, variant, options); rmlvo.model = ""; rmlvo.layout = ""; rmlvo.variant = ""; rmlvo.options = ""; input->keymap = xkb_keymap_new_from_names(input->ctx, &rmlvo, 0); if (!input->keymap) { llog_warn(input, "failed to create XKB default keymap, " "reverting to built-in fallback"); input->keymap = xkb_keymap_new_from_string(input->ctx, fallback, XKB_KEYMAP_FORMAT_TEXT_V1, 0); if (!input->keymap) { llog_error(input, "cannot create fallback keymap"); ret = -EFAULT; goto err_ctx; } } llog_debug(input, "new fallback keyboard description"); } else { llog_debug(input, "new keyboard description (%s, %s, %s, %s)", model, layout, variant, options); } return 0; err_ctx: xkb_context_unref(input->ctx); return ret; } void uxkb_desc_destroy(struct uterm_input *input) { xkb_keymap_unref(input->keymap); xkb_context_unref(input->ctx); } static void timer_event(struct ev_timer *timer, uint64_t num, void *data) { struct uterm_input_dev *dev = data; dev->repeat_event.handled = false; shl_hook_call(dev->input->hook, dev->input, &dev->repeat_event); } int uxkb_dev_init(struct uterm_input_dev *dev) { int ret; ret = ev_eloop_new_timer(dev->input->eloop, &dev->repeat_timer, NULL, timer_event, dev); if (ret) return ret; dev->state = xkb_state_new(dev->input->keymap); if (!dev->state) { llog_error(dev->input, "cannot create XKB state"); ret = -ENOMEM; goto err_timer; } return 0; err_timer: ev_eloop_rm_timer(dev->repeat_timer); return ret; } void uxkb_dev_destroy(struct uterm_input_dev *dev) { xkb_state_unref(dev->state); ev_eloop_rm_timer(dev->repeat_timer); } #define EVDEV_KEYCODE_OFFSET 8 enum { KEY_RELEASED = 0, KEY_PRESSED = 1, KEY_REPEATED = 2, }; static void uxkb_dev_update_keyboard_leds(struct uterm_input_dev *dev) { static const struct { int evdev_led; const char *xkb_led; } leds[] = { { LED_NUML, XKB_LED_NAME_NUM }, { LED_CAPSL, XKB_LED_NAME_CAPS }, { LED_SCROLLL, XKB_LED_NAME_SCROLL }, }; struct input_event events[sizeof(leds) / sizeof(*leds)]; int i, ret; if (!(dev->capabilities & UTERM_DEVICE_HAS_LEDS)) return; memset(events, 0, sizeof(events)); for (i = 0; i < sizeof(leds) / sizeof(*leds); i++) { events[i].type = EV_LED; events[i].code = leds[i].evdev_led; if (xkb_state_led_name_is_active(dev->state, leds[i].xkb_led) > 0) events[i].value = 1; } ret = write(dev->rfd, events, sizeof(events)); if (ret != sizeof(events)) llog_warning(dev->input, "cannot update LED state (%d): %m", errno); } static inline int uxkb_dev_resize_event(struct uterm_input_dev *dev, size_t s) { uint32_t *tmp; if (s > dev->num_syms) { tmp = realloc(dev->event.keysyms, sizeof(uint32_t) * s); if (!tmp) { llog_warning(dev->input, "cannot reallocate keysym buffer"); return -ENOKEY; } dev->event.keysyms = tmp; tmp = realloc(dev->event.codepoints, sizeof(uint32_t) * s); if (!tmp) { llog_warning(dev->input, "cannot reallocate codepoints buffer"); return -ENOKEY; } dev->event.codepoints = tmp; tmp = realloc(dev->repeat_event.keysyms, sizeof(uint32_t) * s); if (!tmp) { llog_warning(dev->input, "cannot reallocate keysym buffer"); return -ENOKEY; } dev->repeat_event.keysyms = tmp; tmp = realloc(dev->repeat_event.codepoints, sizeof(uint32_t) * s); if (!tmp) { llog_warning(dev->input, "cannot reallocate codepoints buffer"); return -ENOKEY; } dev->repeat_event.codepoints = tmp; dev->num_syms = s; } return 0; } static int uxkb_dev_fill_event(struct uterm_input_dev *dev, struct uterm_input_event *ev, xkb_keycode_t code, int num_syms, const xkb_keysym_t *syms) { int ret, i; ret = uxkb_dev_resize_event(dev, num_syms); if (ret) return ret; ev->keycode = code; ev->ascii = shl_get_ascii(dev->state, code, syms, num_syms); ev->mods = shl_get_xkb_mods(dev->state); ev->num_syms = num_syms; memcpy(ev->keysyms, syms, sizeof(uint32_t) * num_syms); for (i = 0; i < num_syms; ++i) { ev->codepoints[i] = xkb_keysym_to_utf32(syms[i]); if (!ev->codepoints[i]) ev->codepoints[i] = UTERM_INPUT_INVALID; } return 0; } static void uxkb_dev_repeat(struct uterm_input_dev *dev, unsigned int state) { struct xkb_keymap *keymap = xkb_state_get_keymap(dev->state); unsigned int i; int num_keysyms, ret; const uint32_t *keysyms; struct itimerspec spec; if (dev->repeating && dev->repeat_event.keycode == dev->event.keycode) { if (state == KEY_RELEASED) { dev->repeating = false; ev_timer_update(dev->repeat_timer, NULL); } return; } if (state == KEY_PRESSED && xkb_keymap_key_repeats(keymap, dev->event.keycode)) { dev->repeat_event.keycode = dev->event.keycode; dev->repeat_event.ascii = dev->event.ascii; dev->repeat_event.mods = dev->event.mods; dev->repeat_event.num_syms = dev->event.num_syms; for (i = 0; i < dev->event.num_syms; ++i) { dev->repeat_event.keysyms[i] = dev->event.keysyms[i]; dev->repeat_event.codepoints[i] = dev->event.codepoints[i]; } } else if (dev->repeating && !xkb_keymap_key_repeats(keymap, dev->event.keycode)) { num_keysyms = xkb_state_key_get_syms(dev->state, dev->repeat_event.keycode, &keysyms); if (num_keysyms <= 0) return; ret = uxkb_dev_fill_event(dev, &dev->repeat_event, dev->repeat_event.keycode, num_keysyms, keysyms); if (ret) return; return; } else { return; } dev->repeating = true; spec.it_interval.tv_sec = 0; spec.it_interval.tv_nsec = dev->input->repeat_rate * 1000000; spec.it_value.tv_sec = 0; spec.it_value.tv_nsec = dev->input->repeat_delay * 1000000; ev_timer_update(dev->repeat_timer, &spec); } int uxkb_dev_process(struct uterm_input_dev *dev, uint16_t key_state, uint16_t code) { struct xkb_state *state; xkb_keycode_t keycode; const xkb_keysym_t *keysyms; int num_keysyms, ret; enum xkb_state_component changed; if (key_state == KEY_REPEATED) return -ENOKEY; state = dev->state; keycode = code + EVDEV_KEYCODE_OFFSET; num_keysyms = xkb_state_key_get_syms(state, keycode, &keysyms); changed = 0; if (key_state == KEY_PRESSED) changed = xkb_state_update_key(state, keycode, XKB_KEY_DOWN); else if (key_state == KEY_RELEASED) changed = xkb_state_update_key(state, keycode, XKB_KEY_UP); if (changed & XKB_STATE_LEDS) uxkb_dev_update_keyboard_leds(dev); if (num_keysyms <= 0) return -ENOKEY; ret = uxkb_dev_fill_event(dev, &dev->event, keycode, num_keysyms, keysyms); if (ret) return -ENOKEY; uxkb_dev_repeat(dev, key_state); if (key_state == KEY_RELEASED) return -ENOKEY; dev->event.handled = false; shl_hook_call(dev->input->hook, dev->input, &dev->event); return 0; } void uxkb_dev_sleep(struct uterm_input_dev *dev) { /* * While the device is asleep, we don't receive key events. This * means that when we wake up, the keyboard state may be different * (e.g. some key is pressed but we don't know about it). This can * cause various problems, like stuck modifiers: consider if we * miss a release of the left Shift key. When the user presses it * again, xkb_state_update_key() will think there is *another* left * Shift key that was pressed. When the key is released, it's as if * this "second" key was released, but the "first" is still left * pressed. * To handle this, when the device goes to sleep, we save our * current knowledge of the keyboard's press/release state. On wake * up, we compare the states before and after, and just feed * xkb_state_update_key() the deltas. */ memset(dev->key_state_bits, 0, sizeof(dev->key_state_bits)); errno = 0; ioctl(dev->rfd, EVIOCGKEY(sizeof(dev->key_state_bits)), dev->key_state_bits); if (errno) llog_warn(dev->input, "failed to save keyboard state (%d): %m", errno); } void uxkb_dev_wake_up(struct uterm_input_dev *dev) { uint32_t code; char *old_bits, cur_bits[sizeof(dev->key_state_bits)]; char old_bit, cur_bit; old_bits = dev->key_state_bits; memset(cur_bits, 0, sizeof(cur_bits)); errno = 0; ioctl(dev->rfd, EVIOCGKEY(sizeof(cur_bits)), cur_bits); if (errno) { llog_warn(dev->input, "failed to get current keyboard state (%d): %m", errno); return; } for (code = 0; code < KEY_CNT; code++) { old_bit = (old_bits[code / 8] & (1 << (code % 8))); cur_bit = (cur_bits[code / 8] & (1 << (code % 8))); if (old_bit == cur_bit) continue; xkb_state_update_key(dev->state, code + EVDEV_KEYCODE_OFFSET, cur_bit ? XKB_KEY_DOWN : XKB_KEY_UP); } uxkb_dev_update_keyboard_leds(dev); }