Linux C - keyMapping keyCodes

时间:2016-01-03 22:19:22

标签: c linux keyboard keycode

我目前正在开发一个用于Linux的GUI(在Raspberry Pi硬件下),我有一个关于键盘的(非常基本的)问题。 我可以通过使用input / eventX文件处理keypressed,keyreleased ...但我得到的唯一的东西是keyCode(显然)。

我想知道,我怎么能映射这些keyCodes来获取它们的字符?

1 个答案:

答案 0 :(得分:2)

此映射由控制台终端的内核完成(请参阅dumpkeys或运行sudo dumpkeys -l以查看当前控制台映射),并通过X服务器为X服务器应用程序完成(使用{{1} }查看映射;第一列没有任何修饰符,第二列是第一个修饰符,第三列是第二个修饰符,第四列是第二个修饰符,第五列是第三个修饰符,依此类推) 。通常,密钥映射文件在man 5 keymaps中描述。

您的问题是,如何自己进行映射。

首先,您需要了解映射的工作原理。在Linux输入子系统中,有9个修饰符,逻辑状态,可以组合以产生2个 9 = 512个不同修饰符状态中的任何一个。它们具有与任何特定键本身相关的的完全任意名称。 (它们是 Shift (2 0 = 1), AltGr (2 1 = 2),控制(2 2 = 4), Alt (2 3 = 8), ShiftL ( 2 4 = 16), ShiftR (2 5 = 32), CtrlL (2 6 < / sup> = 64), CtrlR (2 7 = 128), CapsShift (2 8 = 256 )。将“有效”值相加在一起得到实际的修饰符状态。)

假设您使用xmodmap -pm -pk来描述每个键盘结果; Unicode代码点(字符)为正数,自定义操作键为负数(如“向上光标”,“向下翻页”等)。然后可以使用类似以下最小示例的内容来描述每个键的映射:

int

请注意,上述内容确实是绝对最小值,未经测试。

它不支持Uncaps_Shift(在没有按下大写锁定键的情况下释放大写锁定),并且从keymap文件定义struct mod_one { __u16 down; /* State change when pressed */ __u16 up; /* State change when released */ }; struct key_one { size_t count; /* Number of states for this keycode */ unsigned short *state; /* Array of states */ int *result; /* Array of keyboard_event() return values */ }; struct key_map { /* Modifier keys */ size_t mod_count; __u16 *mod_code; struct mod_one *mod_xor; /* Non-modifier keys */ size_t key_count; __u16 *key_code; struct key_one *key_map; }; int keyboard_event(const struct input_event *const ev, unsigned short *const state, const struct key_map *const map) { /* Note: Uses linear search for simplicity. * In practice, you want the arrays to be sorted, * and use a binary search instead. */ /* Sanity check for NULL pointers. */ if (!ev || !state || !map) return 0; if (ev->type == EV_KEY) { size_t i, k; /* Is it a modifier key? */ for (i = 0; i < map->mod_count; i++) if (ev->code == map->mod_code[i]) { /* Yes. Apply the state change. */ if (ev->code == 0) *state ^= map->mod_xor[i].down; else if (ev->code == 1) *state ^= map->mod_xor[i].up; break; } /* Does this event map to a keypress? * We rely on kernel/device autorepeat. */ if (ev->code == 0 || ev->code == 2) for (i = 0; i < map->key_count; i++) if (ev->key == map->key_code[i]) { for (k = 0; k < map->key_map[i].count; k++) if (*state == map->key_map[i].state[k]) return map->key_map[i].result[k]; } } return 0; } 有点单调乏味。但是,希望您可以看到逻辑(通过使用简单的struct key_map尝试并观察struct key_map更改并返回值)。在一个真实的程序中,我想我会单独处理shift / capslock状态,并且每个修改键使用一个掩码(按下时是键盘状态,释放时是nand(而不是)keystate),带有shift和caps锁定状态是彼此的xor,因此如果修饰符状态以某种方式混淆,则多次点击这些键将始终修复状态。

就个人而言,我建议(作为用户,需求)使用Unicode,而不是ASCII或Windows-1252或ISO-8859- x 。将Unicode代码转换为普通的UTF-8字符串并检查它们占用多少*state是微不足道的。如果在编辑期间使用Unicode字符串,则编辑字符串(尤其是在字符串中移动光标以及删除单个字符)会更容易。 (我更喜欢将UTF-8用于“不可变”字符串,但在编辑小部件时,为简单起见,将它们转换为char或类似.UTF-8⇔UCS4转换很简单。)

(如果您还不知道,字符€在大多数欧洲键盘上可用作AltGr + E.它是Unicode U + 20AC,或代码8364,UTF-8由三个字符表示, wchar_t。如果你不能支持它,不要指望欧洲人使用你的用户界面。不,ISO-8859-15也没有真正削减它,即使它确实有€。)

最后,对于你的UI,我建议你在你的程序中使用一个专用的pthread来读取输入事件设备 - 所有键盘,鼠标和指针 - ,并提供按键(和指针变化的通知)到循环缓冲区,使用原子计数器和信号量,或互斥锁和条件变量来维护线程之间的缓冲区状态。专用线程只需要一个最小的堆栈(16384或32768字节就足够了),并且因为它在大多数时间内阻止等待新事件,所以它根本不会消耗太多资源。

让专用线程维护键盘和指针输入设备(并将指针移动或对输入缓冲区执行某些“字符”)意味着您的程序可以使用简单的循环来检查用户输入;每当用户按下某个键或移动指针时,该循环将迭代。你知道,不需要民意调查。它对于简单的基于帧缓冲的图形界面非常有效。