diff --git a/Makefile.am b/Makefile.am index 2411317..b71aa70 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,7 +48,6 @@ libkmscon_core_la_SOURCES = \ src/log.c src/log.h \ src/eloop.c src/eloop.h \ src/vt.c src/vt.h \ - src/input.c src/input.h \ src/vte.c src/vte.h \ src/terminal.c src/terminal.h \ src/pty.c src/pty.h \ @@ -65,12 +64,10 @@ libkmscon_core_la_SOURCES = \ if USE_XKBCOMMON libkmscon_core_la_SOURCES += \ - src/kbd_xkb.c src/kbd.h \ external/imKStoUCS.c external/imKStoUCS.h \ src/uterm_input_xkb.c else libkmscon_core_la_SOURCES += \ - src/kbd_dumb.c src/kbd.h \ external/imKStoUCS.c external/imKStoUCS.h \ src/uterm_input_dumb.c endif diff --git a/src/input.c b/src/input.c deleted file mode 100644 index 2f420ab..0000000 --- a/src/input.c +++ /dev/null @@ -1,699 +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. - */ - -/* - * The main object kmscon_input discovers and monitors input devices, and - * adds/removes them accordingly from the devices linked list. - * - * The udev monitor keeps running even while the object is in INPUT_ASLEEP. - * We do this because we'll either lose track of the devices, or otherwise - * have to re-scan the devices at every wakeup. - * - * The kmscon_input_device objects hold the file descriptors for their device - * nodes. All events go through the input-object callback; there is currently - * no "routing" or any differentiation between them. When the input is put to - * sleep, all fd's are closed. When woken up, they are opened. There should be - * not spurious events delivered. The initial state depends on the - * kmscon_input's state. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "conf.h" -#include "eloop.h" -#include "input.h" -#include "kbd.h" -#include "log.h" -#include "misc.h" - -#define LOG_SUBSYSTEM "input" - -/* How many longs are needed to hold \n bits. */ -#define NLONGS(n) (((n) + LONG_BIT - 1) / LONG_BIT) - -enum input_state { - INPUT_ASLEEP, - INPUT_AWAKE, -}; - -/* See probe_device_features(). */ -enum device_feature { - FEATURE_HAS_KEYS = 0x01, - FEATURE_HAS_LEDS = 0x02, -}; - -struct kmscon_input_device { - struct kmscon_input_device *next; - struct kmscon_input *input; - - unsigned int features; - - int rfd; - char *devnode; - struct ev_fd *fd; - - struct kmscon_kbd *kbd; -}; - -struct kmscon_input { - size_t ref; - enum input_state state; - struct kmscon_input_device *devices; - - struct ev_eloop *eloop; - struct kmscon_hook *hook; - - struct udev *udev; - struct udev_monitor *monitor; - struct ev_fd *monitor_fd; - - struct kmscon_kbd_desc *desc; -}; - -static void remove_device(struct kmscon_input *input, const char *node); - -static void notify_key(struct kmscon_input_device *device, - uint16_t type, uint16_t code, int32_t value) -{ - int ret; - struct kmscon_input_event ev; - struct kmscon_input *input; - - if (type != EV_KEY) - return; - - input = device->input; - ret = kmscon_kbd_process_key(device->kbd, value, code, &ev); - - if (ret && ret != -ENOKEY) - return; - - if (ret != -ENOKEY) - kmscon_hook_call(input->hook, input, &ev); -} - -static void device_data_arrived(struct ev_fd *fd, int mask, void *data) -{ - int i; - ssize_t len, n; - struct kmscon_input_device *device = data; - struct kmscon_input *input = device->input; - struct input_event ev[16]; - - len = sizeof(ev); - while (len == sizeof(ev)) { - len = read(device->rfd, &ev, sizeof(ev)); - if (len < 0) { - if (errno == EWOULDBLOCK) - break; - - log_warn("reading device %s failed %d", - device->devnode, errno); - remove_device(input, device->devnode); - } else if (len == 0) { - log_debug("EOF device %s", device->devnode); - remove_device(input, device->devnode); - } else if (len % sizeof(*ev)) { - log_warn("read invalid input_event"); - } else { - n = len / sizeof(*ev); - for (i = 0; i < n; i++) - notify_key(device, ev[i].type, ev[i].code, - ev[i].value); - } - } -} - -int kmscon_input_device_wake_up(struct kmscon_input_device *device) -{ - int ret; - unsigned long ledbits[NLONGS(LED_CNT)] = { 0 }; - - if (!device || !device->input || !device->input->eloop) - return -EINVAL; - - if (device->rfd >= 0) - return 0; - - device->rfd = open(device->devnode, O_CLOEXEC | O_NONBLOCK | O_RDONLY); - if (device->rfd < 0) { - log_warn("cannot open input device %s: %d", - device->devnode, errno); - return -errno; - } - - if (device->features & FEATURE_HAS_KEYS) { - if (device->features & FEATURE_HAS_LEDS) { - errno = 0; - ioctl(device->rfd, EVIOCGLED(sizeof(ledbits)), - &ledbits); - if (errno) - log_warn("cannot discover state of LEDs %s: %m", - device->devnode); - } - - /* rediscover the keyboard state if sth changed during sleep */ - kmscon_kbd_reset(device->kbd, ledbits); - - ret = ev_eloop_new_fd(device->input->eloop, &device->fd, - device->rfd, EV_READABLE, - device_data_arrived, device); - if (ret) { - close(device->rfd); - device->rfd = -1; - return ret; - } - } - - return 0; -} - -void kmscon_input_device_sleep(struct kmscon_input_device *device) -{ - if (!device) - return; - - if (device->rfd < 0) - return; - - ev_eloop_rm_fd(device->fd); - device->fd = NULL; - close(device->rfd); - device->rfd = -1; -} - -static int kmscon_input_device_new(struct kmscon_input_device **out, - struct kmscon_input *input, const char *devnode, - unsigned int features) -{ - int ret; - struct kmscon_input_device *device; - - if (!out || !input) - return -EINVAL; - - device = malloc(sizeof(*device)); - if (!device) - return -ENOMEM; - - memset(device, 0, sizeof(*device)); - - device->devnode = strdup(devnode); - if (!device->devnode) { - free(device); - return -ENOMEM; - } - - ret = kmscon_kbd_new(&device->kbd, input->desc); - if (ret) { - free(device->devnode); - free(device); - return ret; - } - - device->input = input; - device->features = features; - device->rfd = -1; - - log_debug("new input device %s", devnode); - *out = device; - return 0; -} - -static void kmscon_input_device_free(struct kmscon_input_device *device) -{ - if (!device) - return; - - log_debug("destroying input device %s", device->devnode); - kmscon_input_device_sleep(device); - kmscon_kbd_unref(device->kbd); - free(device->devnode); - free(device); -} - -int kmscon_input_new(struct kmscon_input **out) -{ - int ret; - struct kmscon_input *input; - - if (!out) - return -EINVAL; - - input = malloc(sizeof(*input)); - if (!input) - return -ENOMEM; - - memset(input, 0, sizeof(*input)); - input->ref = 1; - input->state = INPUT_ASLEEP; - - ret = kmscon_hook_new(&input->hook); - if (ret) - goto err_free; - - ret = kmscon_kbd_desc_new(&input->desc, - conf_global.xkb_layout, - conf_global.xkb_variant, - conf_global.xkb_options); - if (ret) { - log_warn("cannot create xkb description"); - goto err_hook; - } - - input->udev = udev_new(); - if (!input->udev) { - log_warn("cannot create udev object"); - ret = -EFAULT; - goto err_xkb; - } - - input->monitor = udev_monitor_new_from_netlink(input->udev, "udev"); - if (!input->monitor) { - log_warn("cannot create udev monitor"); - ret = -EFAULT; - goto err_udev; - } - - ret = udev_monitor_filter_add_match_subsystem_devtype(input->monitor, - "input", NULL); - if (ret) { - log_warn("cannot add udev filter"); - ret = -EFAULT; - goto err_monitor; - } - - ret = udev_monitor_enable_receiving(input->monitor); - if (ret) { - log_warn("cannot start udev monitor"); - ret = -EFAULT; - goto err_monitor; - } - - log_debug("new input object"); - *out = input; - return 0; - -err_monitor: - udev_monitor_unref(input->monitor); -err_udev: - udev_unref(input->udev); -err_xkb: - kmscon_kbd_desc_unref(input->desc); -err_hook: - kmscon_hook_free(input->hook); -err_free: - free(input); - return ret; -} - -void kmscon_input_ref(struct kmscon_input *input) -{ - if (!input) - return; - - ++input->ref; -} - -void kmscon_input_unref(struct kmscon_input *input) -{ - if (!input || !input->ref || --input->ref) - return; - - log_debug("free input object"); - kmscon_input_disconnect_eloop(input); - udev_monitor_unref(input->monitor); - udev_unref(input->udev); - kmscon_kbd_desc_unref(input->desc); - kmscon_hook_free(input->hook); - free(input); -} - -/* - * See if the device has anything useful to offer. - * We go over the desired features and return a mask of enum device_feature's. - */ -static unsigned int probe_device_features(const char *node) -{ - int i, fd; - unsigned int features = 0; - unsigned long evbits[NLONGS(EV_CNT)] = { 0 }; - unsigned long keybits[NLONGS(KEY_CNT)] = { 0 }; - - fd = open(node, O_NONBLOCK | O_CLOEXEC); - if (fd < 0) - return 0; - - /* Which types of input events the device supports. */ - errno = 0; - ioctl(fd, EVIOCGBIT(0, sizeof(evbits)), evbits); - if (errno) - goto err_ioctl; - - /* Device supports keys/buttons. */ - if (kmscon_evdev_bit_is_set(evbits, EV_KEY)) { - errno = 0; - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits); - if (errno) - goto err_ioctl; - - /* - * If the device support any of the normal keyboard keys, we - * take it. Even if the keys are not ordinary they can be - * mapped to anything by the keyboard backend. - */ - for (i = KEY_RESERVED; i <= KEY_MIN_INTERESTING; i++) { - if (kmscon_evdev_bit_is_set(keybits, i)) { - features |= FEATURE_HAS_KEYS; - break; - } - } - } - - if (kmscon_evdev_bit_is_set(evbits, EV_LED)) - features |= FEATURE_HAS_LEDS; - - close(fd); - return features; - -err_ioctl: - if (errno != ENOTTY) - log_warn("cannot probe features of device (%s): %m", node); - close(fd); - return 0; -} - -static void add_device(struct kmscon_input *input, - struct udev_device *udev_device) -{ - int ret; - struct kmscon_input_device *device; - const char *node, *seat; - unsigned int features; - - if (!input || !udev_device) - return; - - node = udev_device_get_devnode(udev_device); - if (!node) - return; - - seat = udev_device_get_property_value(udev_device, "ID_SEAT"); - if (!seat) - seat = "seat0"; - if (strcmp(seat, conf_global.seat)) { - log_debug("ignoring device %s (wrong seat)", node); - return; - } - - features = probe_device_features(node); - if (!(features & FEATURE_HAS_KEYS)) { - log_debug("ignoring non-useful device %s", node); - return; - } - - ret = kmscon_input_device_new(&device, input, node, features); - if (ret) { - log_warn("cannot create input device for %s", node); - return; - } - - if (input->state == INPUT_AWAKE) { - ret = kmscon_input_device_wake_up(device); - if (ret) { - log_warn("cannot wake up new device %s", node); - kmscon_input_device_free(device); - return; - } - } - - device->next = input->devices; - input->devices = device; - log_debug("added device %s (features: %#x)", node, features); -} - -static void remove_device(struct kmscon_input *input, const char *node) -{ - struct kmscon_input_device *iter, *prev; - - if (!input || !node || !input->devices) - return; - - iter = input->devices; - prev = NULL; - - while (iter) { - if (!strcmp(iter->devnode, node)) { - if (prev == NULL) - input->devices = iter->next; - else - prev->next = iter->next; - - kmscon_input_device_free(iter); - log_debug("removed device %s", node); - break; - } - - prev = iter; - iter = iter->next; - } -} - -static void remove_device_udev(struct kmscon_input *input, - struct udev_device *udev_device) -{ - const char *node; - - node = udev_device_get_devnode(udev_device); - if (!node) - return; - - remove_device(input, node); -} - -static void device_changed(struct ev_fd *fd, int mask, void *data) -{ - struct kmscon_input *input = data; - struct udev_device *udev_device; - const char *action; - - udev_device = udev_monitor_receive_device(input->monitor); - if (!udev_device) - return; - - action = udev_device_get_action(udev_device); - if (!action) { - log_warn("cannot get action field of new device"); - goto err_device; - } - - if (!strcmp(action, "add")) - add_device(input, udev_device); - else if (!strcmp(action, "remove")) - remove_device_udev(input, udev_device); - -err_device: - udev_device_unref(udev_device); -} - -static void add_initial_devices(struct kmscon_input *input) -{ - int ret; - struct udev_enumerate *e; - struct udev_list_entry *first; - struct udev_list_entry *item; - struct udev_device *udev_device; - const char *syspath; - - e = udev_enumerate_new(input->udev); - if (!e) { - log_warn("cannot create udev enumeration"); - return; - } - - ret = udev_enumerate_add_match_subsystem(e, "input"); - if (ret) { - log_warn("cannot add match to udev enumeration"); - goto err_enum; - } - - if (strcmp(conf_global.seat, "seat0")) { - ret = udev_enumerate_add_match_tag(e, conf_global.seat); - if (ret) { - log_warn("cannot add match to udev enumeration"); - goto err_enum; - } - } - - ret = udev_enumerate_scan_devices(e); - if (ret) { - log_warn("cannot scan udev enumeration"); - goto err_enum; - } - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - syspath = udev_list_entry_get_name(item); - if (!syspath) - continue; - - udev_device = udev_device_new_from_syspath(input->udev, syspath); - if (!udev_device) - continue; - - add_device(input, udev_device); - udev_device_unref(udev_device); - } - -err_enum: - udev_enumerate_unref(e); -} - -int kmscon_input_connect_eloop(struct kmscon_input *input, - struct ev_eloop *eloop) -{ - int ret; - int fd; - - if (!input || !eloop) - return -EINVAL; - - if (input->eloop) - return -EALREADY; - - fd = udev_monitor_get_fd(input->monitor); - ret = ev_eloop_new_fd(eloop, &input->monitor_fd, fd, - EV_READABLE, device_changed, input); - if (ret) - return ret; - - ev_eloop_ref(eloop); - input->eloop = eloop; - - add_initial_devices(input); - - return 0; -} - -void kmscon_input_disconnect_eloop(struct kmscon_input *input) -{ - struct kmscon_input_device *tmp; - - if (!input || !input->eloop) - return; - - while (input->devices) { - tmp = input->devices; - input->devices = tmp->next; - kmscon_input_device_free(tmp); - } - - ev_eloop_rm_fd(input->monitor_fd); - input->monitor_fd = NULL; - ev_eloop_unref(input->eloop); - input->eloop = NULL; -} - -int kmscon_input_register_cb(struct kmscon_input *input, kmscon_input_cb cb, - void *data) -{ - if (!input || !cb) - return -EINVAL; - - return kmscon_hook_add_cast(input->hook, cb, data); -} - -void kmscon_input_unregister_cb(struct kmscon_input *input, kmscon_input_cb cb, - void *data) -{ - if (!input || !cb) - return; - - kmscon_hook_rm_cast(input->hook, cb, data); -} - -void kmscon_input_sleep(struct kmscon_input *input) -{ - struct kmscon_input_device *iter; - - if (!input || input->state == INPUT_ASLEEP) - return; - - log_debug("going asleep"); - - for (iter = input->devices; iter; iter = iter->next) - kmscon_input_device_sleep(iter); - - input->state = INPUT_ASLEEP; -} - -void kmscon_input_wake_up(struct kmscon_input *input) -{ - struct kmscon_input_device *iter, *prev, *tmp; - int ret; - - if (!input || input->state == INPUT_AWAKE) - return; - - log_debug("waking up"); - prev = NULL; - iter = input->devices; - - while (iter) { - ret = kmscon_input_device_wake_up(iter); - if (ret) { - if (!prev) - input->devices = iter->next; - else - prev->next = iter->next; - - tmp = iter; - iter = iter->next; - - log_warn("device %s does not wake up, removing device", - tmp->devnode); - kmscon_input_device_free(tmp); - } else { - prev = iter; - iter = iter->next; - } - } - - input->state = INPUT_AWAKE; -} - -bool kmscon_input_is_asleep(struct kmscon_input *input) -{ - if (!input) - return false; - - return input->state == INPUT_ASLEEP; -} diff --git a/src/input.h b/src/input.h deleted file mode 100644 index 31e3ddc..0000000 --- a/src/input.h +++ /dev/null @@ -1,103 +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. - */ - -/* - * This module provides an input object which can deliver all useful input - * events to the program. - * - * Its use should be as simple as the following (but also see below): - * - Create a new input object. - * - Provide a callback function to receive the events. - * - Connect the input object to a ev_eloop. - * - Wake up the input object to begin receiving input events through the - * event loop. - * - * A few things to note: - * - This module uses evdev for input, and reads from input devices directly. - * This requires root privileges; waking up the input object will fail - * without them. - * - evdev has no inhert notion of "focus" like tty input. In other words, - * it will deliver input events whether they are intended for the program - * or not. This may also pose a security risk. Therefore, make sure to put - * the object to sleep when the program is not active, for example by - * reacting to VT changes. - */ - -#ifndef KMSCON_INPUT_H -#define KMSCON_INPUT_H - -#include -#include -#include -#include "eloop.h" - -struct kmscon_input; - -enum kmscon_modifier { - KMSCON_SHIFT_MASK = (1 << 0), - KMSCON_LOCK_MASK = (1 << 1), - KMSCON_CONTROL_MASK = (1 << 2), - KMSCON_MOD1_MASK = (1 << 3), - KMSCON_MOD2_MASK = (1 << 4), - KMSCON_MOD3_MASK = (1 << 5), - KMSCON_MOD4_MASK = (1 << 6), - KMSCON_MOD5_MASK = (1 << 7), -}; - -#define KMSCON_INPUT_INVALID 0xffffffff - -struct kmscon_input_event { - uint16_t keycode; /* linux keycode - KEY_* - linux/input.h */ - uint32_t keysym; /* X keysym - XK_* - X11/keysym.h */ - unsigned int mods; /* active modifiers - kmscon_modifier mask */ - uint32_t unicode; /* UCS-4 unicode value or KMSCON_INPUT_INVALID */ -}; - -typedef void (*kmscon_input_cb) (struct kmscon_input *input, - struct kmscon_input_event *ev, void *data); - -int kmscon_input_new(struct kmscon_input **out); -void kmscon_input_ref(struct kmscon_input *input); -void kmscon_input_unref(struct kmscon_input *input); - -int kmscon_input_connect_eloop(struct kmscon_input *input, - struct ev_eloop *eloop); -void kmscon_input_disconnect_eloop(struct kmscon_input *input); -int kmscon_input_register_cb(struct kmscon_input *input, kmscon_input_cb cb, - void *data); -void kmscon_input_unregister_cb(struct kmscon_input *input, kmscon_input_cb cb, - void *data); - -void kmscon_input_sleep(struct kmscon_input *input); -void kmscon_input_wake_up(struct kmscon_input *input); -bool kmscon_input_is_asleep(struct kmscon_input *input); - -/* Querying the results of evdev ioctl's. Also used by kbd backends. */ -static inline bool kmscon_evdev_bit_is_set(const unsigned long *array, int bit) -{ - return !!(array[bit / LONG_BIT] & (1LL << (bit % LONG_BIT))); -} - -#endif /* KMSCON_INPUT_H */ diff --git a/src/kbd.h b/src/kbd.h deleted file mode 100644 index b1d8476..0000000 --- a/src/kbd.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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, const unsigned long *ledbits); - -/* - * 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 deleted file mode 100644 index 2cfa9d8..0000000 --- a/src/kbd_dumb.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - * 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" - -#define LOG_SUBSYSTEM "kbd_dumb" - -/* - * 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->ref = 1; - - 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, const unsigned long *ledbits) -{ - if (!kbd) - return; - - kbd->mods = 0; - - if (kmscon_evdev_bit_is_set(ledbits, LED_NUML)) - kbd->mods |= KMSCON_MOD2_MASK; - if (kmscon_evdev_bit_is_set(ledbits, LED_CAPSL)) - kbd->mods |= KMSCON_LOCK_MASK; -} - -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; - - desc = malloc(sizeof(*desc)); - if (!desc) - return -ENOMEM; - - memset(desc, 0, sizeof(*desc)); - desc->ref = 1; - - log_debug("new keyboard description (%s, %s, %s)", - layout, variant, options); - *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("destroying keyboard description"); - - 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/kbd_xkb.c b/src/kbd_xkb.c deleted file mode 100644 index f369fa1..0000000 --- a/src/kbd_xkb.c +++ /dev/null @@ -1,1012 +0,0 @@ -/* - * kmscon - translating key presses to input events using libxkbcommon - * - * 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. - */ - -/* - * This mostly involves things the X server does normally and libxkbcommon - * doesn't provide us for free. - * This implements a minimal subset of XKB, mostly ignoring stuff like: - * - The protocol itself - we don't allow changing/querying and do everything - * at init. - * - Everything to do with pointing devices, buttons.. - * - Indicators - * - Controls - * - Bells - * - Dead keys - * - All actions beside group- and modifier-related. - * - Behaviours - * - Geometries - * - And various tweaks to what we do support. - * - * Some references to understand what's going on: - * - * [Lib] The X Keyboard Extension: Library Specification - * http://www.x.org/releases/current/doc/libX11/specs/XKB/xkblib.html - * [Proto] The X Keyboard Extension: Protocol Specification - * http://www.x.org/releases/current/doc/kbproto/xkbproto.html - * [xserver] The X server source code dealing with xkb - * /xkb/ - * [xlib] The Xlib source code dealing with xkb and input methods - * /xkb/ - * /modules/im/ximcp/ - * [Pascal] Some XKB documentation by its maintainer (not the best english) - * http://pascal.tsu.ru/en/xkb/ - * [Headers] Some XKB-related headers - * /usr/include/X11/extensions/XKBcommon.h - * /usr/include/X11/extensions/XKB.h - * /usr/include/X11/keysymdef.h - */ - -#include -#include -#include -#include -#include -#include - -#include "kbd.h" -#include "log.h" -#include "imKStoUCS.h" - -#define LOG_SUBSYSTEM "kbd_xkb" - -struct kmscon_kbd_desc { - unsigned long ref; - - struct xkb_desc *desc; -}; - -struct kmscon_kbd { - unsigned long ref; - struct kmscon_kbd_desc *desc; - - struct xkb_state state; -}; - -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->ref = 1; - - 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); -} - -static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods) -{ - int i; - uint32_t bit; - uint8_t mods; - - mods = 0x00; - - for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1) - if (vmods & bit) - mods |= desc->server->vmods[i]; - - return mods; -} - -static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc, - uint16_t vmods, uint8_t real_mods) -{ - uint8_t mods = 0x00; - - mods |= real_mods; - mods |= virtual_to_real_mods(desc, vmods); - - return mods; -} - -/* - * Helper function for the wrap_group_* functions. - * See [Lib] 11.7.1 for the rules. - */ -static uint8_t wrap_group(int16_t group, int num_groups, uint8_t group_info) -{ - /* No need for wrapping. */ - if (XkbIsLegalGroup(group) && group < num_groups) - return group; - - switch (XkbOutOfRangeGroupAction(group_info)) { - case XkbWrapIntoRange: - /* - * C99 says a negative dividend in a modulo operation - * will always give a negative result. - */ - if (group < 0) - return num_groups + (group % num_groups); - else - return group % num_groups; - - case XkbClampIntoRange: - /* This one seems to be unused. */ - return num_groups - 1; - - case XkbRedirectIntoRange: - /* This one seems to be unused. */ - group = XkbOutOfRangeGroupNumber(group_info); - /* If it's _still_ out of range, use the first group. */ - if (group >= num_groups) - return 0; - } - - return 0; -} - -/* - * Wrap an arbitrary group into a legal effective global group according to - * the GroupsWrap control. - * (Group actions mostly act on the group number in a relative manner [e.g. - * +1, -1]. So if we have N groups, the effective group is N-1, and we get a - * SetGroup +1, this tells us what to do.) - */ -static uint8_t wrap_group_control(struct xkb_desc *desc, int16_t group) -{ - int num_groups; - uint8_t group_info; - - num_groups = desc->ctrls->num_groups; - group_info = desc->ctrls->groups_wrap; - - return wrap_group(group, num_groups, group_info); -} - -/* - * Wrap the effective global group to a legal group for the keycode, according - * to the rule specified for the key. - * (Some keycodes may have more groups than others, and so the effective - * group may not make sense for a certain keycode). - */ -static uint8_t wrap_group_keycode(struct xkb_desc *desc, xkb_keycode_t keycode, - int16_t group) -{ - int num_groups; - uint8_t group_info; - - num_groups = XkbKeyNumGroups(desc, keycode); - group_info = XkbKeyGroupInfo(desc, keycode); - - return wrap_group(group, num_groups, group_info); -} - -/* - * Need to update the effective mods after any changes to the base, latched or - * locked mods. - */ -static void update_effective_mods(struct xkb_desc *desc, - struct xkb_state *state) -{ - state->mods = state->base_mods | state->latched_mods | - state->locked_mods; -} - -/* - * Need to update the effective group after any changes to the base, latched or - * locked group. - */ -static void update_effective_group(struct xkb_desc *desc, - struct xkb_state *state) -{ - int16_t group; - - /* Update the effective group. */ - 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, - xkb_keycode_t 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, - xkb_keycode_t 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, - xkb_keycode_t 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, xkb_keycode_t 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 = XkbKeyType(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, xkb_keycode_t 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; - xkb_keycode_t 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 && !state_changed) { - 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; - - for (i=0; i < XkbNumIndicators; i++) - if (!strcmp(desc->names->indicators[i], indicator_name)) - 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, const unsigned long *ledbits) -{ - unsigned int i; - struct xkb_desc *desc; - struct xkb_state *state; - struct xkb_indicator_map *im; - static const struct { - int led; - const char *indicator_name; - } led_names[] = { - { LED_NUML, "Num Lock" }, - { LED_CAPSL, "Caps Lock" }, - { LED_SCROLLL, "Scroll Lock" }, - { LED_COMPOSE, "Compose" }, - }; - - 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; - - for (i = 0; i < sizeof(led_names) / sizeof(*led_names); i++) { - if (!kmscon_evdev_bit_is_set(ledbits, led_names[i].led)) - continue; - - im = find_indicator_map(desc, led_names[i].indicator_name); - - /* 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) -{ - struct xkb_server_map *server; - int sym_count; - unsigned short index, new_size_acts; - union xkb_action *acts; - - server = desc->server; - sym_count = XkbKeyNumSyms(desc, keycode); - - if (XkbKeyHasActions(desc, keycode)) - return 0; - - index = server->num_acts; - - /* num_acts is the occupied slots, size_acts is the capacity. */ - if (server->num_acts + sym_count > server->size_acts) { - /* - * Don't have enough space, need to allocate. We add some - * extra to avoid repeated reallocs. - */ - new_size_acts = server->num_acts + sym_count + 8; - acts = realloc(server->acts, new_size_acts * sizeof (*acts)); - if (!acts) - return -ENOMEM; - server->acts = acts; - server->size_acts = new_size_acts; - } - - /* XkbSA_NoAction is 0x00 so we're good. */ - memset(&server->acts[index], 0, sym_count * sizeof(*server->acts)); - server->key_acts[keycode] = index; - server->num_acts += sym_count; - - return 0; -} - -static int init_compat_for_keysym(struct xkb_desc *desc, xkb_keycode_t 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, xkb_keycode_t 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; - struct xkb_rule_names rmlvo = { - .rules = "evdev", - .model = "evdev", - .layout = layout, - .variant = variant, - .options = options, - }; - - if (!out) - return -EINVAL; - - desc = malloc(sizeof(*desc)); - if (!desc) - return -ENOMEM; - - memset(desc, 0, sizeof(*desc)); - desc->ref = 1; - - desc->desc = xkb_compile_keymap_from_rules(&rmlvo); - if (!desc->desc) { - log_err("cannot compile keymap from rules"); - 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); - - log_debug("new keyboard description (%s, %s, %s)", - layout, variant, options); - *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("destroying keyboard description"); - 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/src/main.c b/src/main.c index d7a190b..600324f 100644 --- a/src/main.c +++ b/src/main.c @@ -31,7 +31,6 @@ #include #include "conf.h" #include "eloop.h" -#include "input.h" #include "log.h" #include "misc.h" #include "ui.h"