We need a copy of xkbcommon-keysyms.h as long as xkbcommon is not included in all major distributions. We _need_ this build-time dependency, otherwise, we cannot build the other keyboard backends. However, requiring xkbcommon as build-time dependency is not a solution as no major distribution includes it. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
454 lines
13 KiB
C
454 lines
13 KiB
C
/*
|
|
* uterm - Linux User-Space Terminal
|
|
*
|
|
* Copyright (c) 2012 Ran Benita <ran234@gmail.com>
|
|
* Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* This 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.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <linux/input.h>
|
|
#include "imKStoUCS.h"
|
|
#include "log.h"
|
|
#include "uterm.h"
|
|
#include "uterm_input.h"
|
|
|
|
#define LOG_SUBSYSTEM "input_plain"
|
|
|
|
/*
|
|
* 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] = XKB_KEY_Escape,
|
|
[KEY_1] = XKB_KEY_1,
|
|
[KEY_2] = XKB_KEY_2,
|
|
[KEY_3] = XKB_KEY_3,
|
|
[KEY_4] = XKB_KEY_4,
|
|
[KEY_5] = XKB_KEY_5,
|
|
[KEY_6] = XKB_KEY_6,
|
|
[KEY_7] = XKB_KEY_7,
|
|
[KEY_8] = XKB_KEY_8,
|
|
[KEY_9] = XKB_KEY_9,
|
|
[KEY_0] = XKB_KEY_0,
|
|
[KEY_MINUS] = XKB_KEY_minus,
|
|
[KEY_EQUAL] = XKB_KEY_equal,
|
|
[KEY_BACKSPACE] = XKB_KEY_BackSpace,
|
|
[KEY_TAB] = XKB_KEY_Tab,
|
|
[KEY_Q] = XKB_KEY_q,
|
|
[KEY_W] = XKB_KEY_w,
|
|
[KEY_E] = XKB_KEY_e,
|
|
[KEY_R] = XKB_KEY_r,
|
|
[KEY_T] = XKB_KEY_t,
|
|
[KEY_Y] = XKB_KEY_y,
|
|
[KEY_U] = XKB_KEY_u,
|
|
[KEY_I] = XKB_KEY_i,
|
|
[KEY_O] = XKB_KEY_o,
|
|
[KEY_P] = XKB_KEY_p,
|
|
[KEY_LEFTBRACE] = XKB_KEY_bracketleft,
|
|
[KEY_RIGHTBRACE] = XKB_KEY_bracketright,
|
|
[KEY_ENTER] = XKB_KEY_Return,
|
|
[KEY_LEFTCTRL] = XKB_KEY_Control_L,
|
|
[KEY_A] = XKB_KEY_a,
|
|
[KEY_S] = XKB_KEY_s,
|
|
[KEY_D] = XKB_KEY_d,
|
|
[KEY_F] = XKB_KEY_f,
|
|
[KEY_G] = XKB_KEY_g,
|
|
[KEY_H] = XKB_KEY_h,
|
|
[KEY_J] = XKB_KEY_j,
|
|
[KEY_K] = XKB_KEY_k,
|
|
[KEY_L] = XKB_KEY_l,
|
|
[KEY_SEMICOLON] = XKB_KEY_semicolon,
|
|
[KEY_APOSTROPHE] = XKB_KEY_apostrophe,
|
|
[KEY_GRAVE] = XKB_KEY_grave,
|
|
[KEY_LEFTSHIFT] = XKB_KEY_Shift_L,
|
|
[KEY_BACKSLASH] = XKB_KEY_backslash,
|
|
[KEY_Z] = XKB_KEY_z,
|
|
[KEY_X] = XKB_KEY_x,
|
|
[KEY_C] = XKB_KEY_c,
|
|
[KEY_V] = XKB_KEY_v,
|
|
[KEY_B] = XKB_KEY_b,
|
|
[KEY_N] = XKB_KEY_n,
|
|
[KEY_M] = XKB_KEY_m,
|
|
[KEY_COMMA] = XKB_KEY_comma,
|
|
[KEY_DOT] = XKB_KEY_period,
|
|
[KEY_SLASH] = XKB_KEY_slash,
|
|
[KEY_RIGHTSHIFT] = XKB_KEY_Shift_R,
|
|
[KEY_KPASTERISK] = XKB_KEY_KP_Multiply,
|
|
[KEY_LEFTALT] = XKB_KEY_Alt_L,
|
|
[KEY_SPACE] = XKB_KEY_space,
|
|
[KEY_CAPSLOCK] = XKB_KEY_Caps_Lock,
|
|
[KEY_F1] = XKB_KEY_F1,
|
|
[KEY_F2] = XKB_KEY_F2,
|
|
[KEY_F3] = XKB_KEY_F3,
|
|
[KEY_F4] = XKB_KEY_F4,
|
|
[KEY_F5] = XKB_KEY_F5,
|
|
[KEY_F6] = XKB_KEY_F6,
|
|
[KEY_F7] = XKB_KEY_F7,
|
|
[KEY_F8] = XKB_KEY_F8,
|
|
[KEY_F9] = XKB_KEY_F9,
|
|
[KEY_F10] = XKB_KEY_F10,
|
|
[KEY_NUMLOCK] = XKB_KEY_Num_Lock,
|
|
[KEY_SCROLLLOCK] = XKB_KEY_Scroll_Lock,
|
|
[KEY_KP7] = XKB_KEY_KP_Home,
|
|
[KEY_KP8] = XKB_KEY_KP_Up,
|
|
[KEY_KP9] = XKB_KEY_KP_Page_Up,
|
|
[KEY_KPMINUS] = XKB_KEY_KP_Subtract,
|
|
[KEY_KP4] = XKB_KEY_KP_Left,
|
|
[KEY_KP5] = XKB_KEY_KP_Begin,
|
|
[KEY_KP6] = XKB_KEY_KP_Right,
|
|
[KEY_KPPLUS] = XKB_KEY_KP_Add,
|
|
[KEY_KP1] = XKB_KEY_KP_End,
|
|
[KEY_KP2] = XKB_KEY_KP_Down,
|
|
[KEY_KP3] = XKB_KEY_KP_Page_Down,
|
|
[KEY_KP0] = XKB_KEY_KP_Insert,
|
|
[KEY_KPDOT] = XKB_KEY_KP_Delete,
|
|
[KEY_F11] = XKB_KEY_F11,
|
|
[KEY_F12] = XKB_KEY_F12,
|
|
[KEY_KPENTER] = XKB_KEY_KP_Enter,
|
|
[KEY_RIGHTCTRL] = XKB_KEY_Control_R,
|
|
[KEY_KPSLASH] = XKB_KEY_KP_Divide,
|
|
[KEY_RIGHTALT] = XKB_KEY_Alt_R,
|
|
[KEY_LINEFEED] = XKB_KEY_Linefeed,
|
|
[KEY_HOME] = XKB_KEY_Home,
|
|
[KEY_UP] = XKB_KEY_Up,
|
|
[KEY_PAGEUP] = XKB_KEY_Page_Up,
|
|
[KEY_LEFT] = XKB_KEY_Left,
|
|
[KEY_RIGHT] = XKB_KEY_Right,
|
|
[KEY_END] = XKB_KEY_End,
|
|
[KEY_DOWN] = XKB_KEY_Down,
|
|
[KEY_PAGEDOWN] = XKB_KEY_Page_Down,
|
|
[KEY_INSERT] = XKB_KEY_Insert,
|
|
[KEY_DELETE] = XKB_KEY_Delete,
|
|
[KEY_KPEQUAL] = XKB_KEY_KP_Equal,
|
|
[KEY_LEFTMETA] = XKB_KEY_Meta_L,
|
|
[KEY_RIGHTMETA] = XKB_KEY_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] = XKB_KEY_KP_7,
|
|
[KEY_KP8] = XKB_KEY_KP_8,
|
|
[KEY_KP9] = XKB_KEY_KP_9,
|
|
[KEY_KP4] = XKB_KEY_KP_4,
|
|
[KEY_KP5] = XKB_KEY_KP_5,
|
|
[KEY_KP6] = XKB_KEY_KP_6,
|
|
[KEY_KP1] = XKB_KEY_KP_1,
|
|
[KEY_KP2] = XKB_KEY_KP_2,
|
|
[KEY_KP3] = XKB_KEY_KP_3,
|
|
[KEY_KP0] = XKB_KEY_KP_0,
|
|
};
|
|
|
|
static const uint32_t keytab_shift[KEYTAB_SIZE] = {
|
|
[KEY_1] = XKB_KEY_exclam,
|
|
[KEY_2] = XKB_KEY_at,
|
|
[KEY_3] = XKB_KEY_numbersign,
|
|
[KEY_4] = XKB_KEY_dollar,
|
|
[KEY_5] = XKB_KEY_percent,
|
|
[KEY_6] = XKB_KEY_asciicircum,
|
|
[KEY_7] = XKB_KEY_ampersand,
|
|
[KEY_8] = XKB_KEY_asterisk,
|
|
[KEY_9] = XKB_KEY_parenleft,
|
|
[KEY_0] = XKB_KEY_parenright,
|
|
[KEY_MINUS] = XKB_KEY_underscore,
|
|
[KEY_EQUAL] = XKB_KEY_plus,
|
|
[KEY_Q] = XKB_KEY_Q,
|
|
[KEY_W] = XKB_KEY_W,
|
|
[KEY_E] = XKB_KEY_E,
|
|
[KEY_R] = XKB_KEY_R,
|
|
[KEY_T] = XKB_KEY_T,
|
|
[KEY_Y] = XKB_KEY_Y,
|
|
[KEY_U] = XKB_KEY_U,
|
|
[KEY_I] = XKB_KEY_I,
|
|
[KEY_O] = XKB_KEY_O,
|
|
[KEY_P] = XKB_KEY_P,
|
|
[KEY_LEFTBRACE] = XKB_KEY_braceleft,
|
|
[KEY_RIGHTBRACE] = XKB_KEY_braceright,
|
|
[KEY_A] = XKB_KEY_A,
|
|
[KEY_S] = XKB_KEY_S,
|
|
[KEY_D] = XKB_KEY_D,
|
|
[KEY_F] = XKB_KEY_F,
|
|
[KEY_G] = XKB_KEY_G,
|
|
[KEY_H] = XKB_KEY_H,
|
|
[KEY_J] = XKB_KEY_J,
|
|
[KEY_K] = XKB_KEY_K,
|
|
[KEY_L] = XKB_KEY_L,
|
|
[KEY_SEMICOLON] = XKB_KEY_colon,
|
|
[KEY_APOSTROPHE] = XKB_KEY_quotedbl,
|
|
[KEY_GRAVE] = XKB_KEY_asciitilde,
|
|
[KEY_BACKSLASH] = XKB_KEY_bar,
|
|
[KEY_Z] = XKB_KEY_Z,
|
|
[KEY_X] = XKB_KEY_X,
|
|
[KEY_C] = XKB_KEY_C,
|
|
[KEY_V] = XKB_KEY_V,
|
|
[KEY_B] = XKB_KEY_B,
|
|
[KEY_N] = XKB_KEY_N,
|
|
[KEY_M] = XKB_KEY_M,
|
|
[KEY_COMMA] = XKB_KEY_less,
|
|
[KEY_DOT] = XKB_KEY_greater,
|
|
[KEY_SLASH] = XKB_KEY_question,
|
|
};
|
|
|
|
static const uint32_t keytab_capslock[KEYTAB_SIZE] = {
|
|
[KEY_Q] = XKB_KEY_Q,
|
|
[KEY_W] = XKB_KEY_W,
|
|
[KEY_E] = XKB_KEY_E,
|
|
[KEY_R] = XKB_KEY_R,
|
|
[KEY_T] = XKB_KEY_T,
|
|
[KEY_Y] = XKB_KEY_Y,
|
|
[KEY_U] = XKB_KEY_U,
|
|
[KEY_I] = XKB_KEY_I,
|
|
[KEY_O] = XKB_KEY_O,
|
|
[KEY_P] = XKB_KEY_P,
|
|
[KEY_A] = XKB_KEY_A,
|
|
[KEY_S] = XKB_KEY_S,
|
|
[KEY_D] = XKB_KEY_D,
|
|
[KEY_F] = XKB_KEY_F,
|
|
[KEY_G] = XKB_KEY_G,
|
|
[KEY_H] = XKB_KEY_H,
|
|
[KEY_J] = XKB_KEY_J,
|
|
[KEY_K] = XKB_KEY_K,
|
|
[KEY_L] = XKB_KEY_L,
|
|
[KEY_Z] = XKB_KEY_Z,
|
|
[KEY_X] = XKB_KEY_X,
|
|
[KEY_C] = XKB_KEY_C,
|
|
[KEY_V] = XKB_KEY_V,
|
|
[KEY_B] = XKB_KEY_B,
|
|
[KEY_N] = XKB_KEY_N,
|
|
[KEY_M] = XKB_KEY_M,
|
|
};
|
|
|
|
static const struct {
|
|
unsigned int mod;
|
|
enum {
|
|
MOD_NORMAL = 1,
|
|
MOD_LOCK,
|
|
} type;
|
|
} modmap[KEYTAB_SIZE] = {
|
|
[KEY_LEFTCTRL] = { UTERM_CONTROL_MASK, MOD_NORMAL },
|
|
[KEY_LEFTSHIFT] = { UTERM_SHIFT_MASK, MOD_NORMAL },
|
|
[KEY_RIGHTSHIFT] = { UTERM_SHIFT_MASK, MOD_NORMAL },
|
|
[KEY_LEFTALT] = { UTERM_MOD1_MASK, MOD_NORMAL },
|
|
[KEY_CAPSLOCK] = { UTERM_LOCK_MASK, MOD_LOCK },
|
|
[KEY_NUMLOCK] = { UTERM_MOD2_MASK, MOD_LOCK },
|
|
[KEY_RIGHTCTRL] = { UTERM_CONTROL_MASK, MOD_NORMAL },
|
|
[KEY_RIGHTALT] = { UTERM_MOD1_MASK, MOD_NORMAL },
|
|
[KEY_LEFTMETA] = { UTERM_MOD4_MASK, MOD_NORMAL },
|
|
[KEY_RIGHTMETA] = { UTERM_MOD4_MASK, MOD_NORMAL },
|
|
};
|
|
|
|
static void plain_dev_ref(struct kbd_dev *kbd)
|
|
{
|
|
if (!kbd || !kbd->ref)
|
|
return;
|
|
|
|
++kbd->ref;
|
|
}
|
|
|
|
static void plain_dev_unref(struct kbd_dev *kbd)
|
|
{
|
|
if (!kbd || !kbd->ref || --kbd->ref)
|
|
return;
|
|
|
|
free(kbd);
|
|
}
|
|
|
|
static void plain_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits)
|
|
{
|
|
if (!kbd)
|
|
return;
|
|
|
|
kbd->plain.mods = 0;
|
|
|
|
if (input_bit_is_set(ledbits, LED_NUML))
|
|
kbd->plain.mods |= UTERM_MOD2_MASK;
|
|
if (input_bit_is_set(ledbits, LED_CAPSL))
|
|
kbd->plain.mods |= UTERM_LOCK_MASK;
|
|
}
|
|
|
|
static int plain_dev_process(struct kbd_dev *kbd,
|
|
uint16_t key_state,
|
|
uint16_t code,
|
|
struct uterm_input_event *out)
|
|
{
|
|
uint32_t keysym;
|
|
unsigned int mod;
|
|
int mod_type;
|
|
|
|
if (!kbd)
|
|
return -EINVAL;
|
|
|
|
/* 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 == 1) {
|
|
if (mod_type == MOD_NORMAL)
|
|
kbd->plain.mods |= mod;
|
|
else if (mod_type == MOD_LOCK)
|
|
kbd->plain.mods ^= mod;
|
|
} else if (key_state == 0) {
|
|
if (mod_type == MOD_NORMAL)
|
|
kbd->plain.mods &= ~mod;
|
|
}
|
|
|
|
/* Don't deliver events purely for modifiers. */
|
|
return -ENOKEY;
|
|
}
|
|
|
|
if (key_state == 0)
|
|
return -ENOKEY;
|
|
|
|
keysym = 0;
|
|
|
|
if (!keysym && kbd->plain.mods & UTERM_MOD2_MASK)
|
|
keysym = keytab_numlock[code];
|
|
if (!keysym && kbd->plain.mods & UTERM_SHIFT_MASK)
|
|
keysym = keytab_shift[code];
|
|
if (!keysym && kbd->plain.mods & UTERM_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) ?: UTERM_INPUT_INVALID;
|
|
out->mods = kbd->plain.mods;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plain_desc_init(struct kbd_desc **out,
|
|
const char *layout,
|
|
const char *variant,
|
|
const char *options)
|
|
{
|
|
struct kbd_desc *desc;
|
|
|
|
if (!out)
|
|
return -EINVAL;
|
|
|
|
desc = malloc(sizeof(*desc));
|
|
if (!desc)
|
|
return -ENOMEM;
|
|
memset(desc, 0, sizeof(*desc));
|
|
desc->ops = &plain_desc_ops;
|
|
|
|
log_debug("new keyboard description (%s, %s, %s)",
|
|
layout, variant, options);
|
|
*out = desc;
|
|
return 0;
|
|
}
|
|
|
|
static void plain_desc_ref(struct kbd_desc *desc)
|
|
{
|
|
if (!desc || !desc->ref)
|
|
return;
|
|
|
|
++desc->ref;
|
|
}
|
|
|
|
static void plain_desc_unref(struct kbd_desc *desc)
|
|
{
|
|
if (!desc || !desc->ref || --desc->ref)
|
|
return;
|
|
|
|
log_debug("destroying keyboard description");
|
|
free(desc);
|
|
}
|
|
|
|
static int plain_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out)
|
|
{
|
|
struct kbd_dev *kbd;
|
|
|
|
kbd = malloc(sizeof(*kbd));
|
|
if (!kbd)
|
|
return -ENOMEM;
|
|
memset(kbd, 0, sizeof(*kbd));
|
|
kbd->ref = 1;
|
|
kbd->ops = &plain_dev_ops;
|
|
|
|
*out = kbd;
|
|
return 0;
|
|
}
|
|
|
|
static void plain_keysym_to_string(uint32_t keysym, char *str, size_t size)
|
|
{
|
|
snprintf(str, size, "%#x", keysym);
|
|
}
|
|
|
|
int plain_string_to_keysym(const char *n, uint32_t *out)
|
|
{
|
|
/* TODO: we really need to implement this; maybe use a hashtable similar
|
|
* to the Xlib? */
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
const struct kbd_desc_ops plain_desc_ops = {
|
|
.init = plain_desc_init,
|
|
.ref = plain_desc_ref,
|
|
.unref = plain_desc_unref,
|
|
.alloc = plain_desc_alloc,
|
|
.keysym_to_string = plain_keysym_to_string,
|
|
.string_to_keysym = plain_string_to_keysym,
|
|
};
|
|
|
|
const struct kbd_dev_ops plain_dev_ops = {
|
|
.ref = plain_dev_ref,
|
|
.unref = plain_dev_unref,
|
|
.reset = plain_dev_reset,
|
|
.process = plain_dev_process,
|
|
};
|