这是一个以前没有了解过的一个盲区。
源于在制作一个带有特殊键的键盘,键盘厂商让挑选择殊键的实现方法。并举例说明了普通按键z在他们的MCU中其实是00 00 1d 00 00 00 00 00(以下简称:MCU键值)即0x1d十进制29,我立即通过hexdump /dev/input/eventX方法查看键盘上z键的在Linux内核上传过来的值(以下简称:Linux键值),一看是0x2c(十进制:44)。那么这两个值是什么关系呢?
于是乎我赶紧Google一番,更离奇的是Windows系统中将z键定义为90(以下简称:Windows键值)。这下出现了三套键值,事情变得复杂又觉得有些清晰了。
既然同一个键盘可以在Linux和Windows下正常使用,那么他们之间必须有一个共用的标准。先抛开Windows不说,看看在Linux内核中是如何将29转换为44的。
定位到Linux内核中usb键盘的驱动程序是drivers/hid/usbhid/usbkbd.c,然后上来就搜索[44],结果真就出来了一张"表",且经过分析后29恰恰是44所在位置的坐标。
Google搜索《 USB HID to PS/2 Scan Code Translation Table》()[注1]就能找到微软整理的一个表。第三列为HID协议中规定的原始键值码。Linux系统中的HID驱动也是将HID设备发来的原始键值码转换为自己系统所制定的码值。详情见:drivers/hid/usbhid/usbkbd.c源码。
MCU键值 学名为:HID Usage Page,Linux或者Windows系统以其为基准对应出自己的系统所使用的系统键值码。
了解过这个情况后,以后在定制键盘时,告知给键盘设计厂商的应该是HID原始键值码。
Linux下对键盘输入事件的调试
日期:20150402
第一行中的getevent是截取键盘输入的命令,该命令运行后先首先将所以的输入设备列出来,比如图上就有键鼠,物理按键,耳机hook,红外遥控按键等几种类型的输入设备,这里只讨论标准键盘输入。
列出输入设备以下的内容就是按键后截取的信息,它们分别是TYPE CODEVALUE,其中TYPE常见的类型有:更多解释见《Linux inputevent-codes》
* EV_SYN:
-Used as markers to separate events. Events may be separated in time or in
space, such as with the multitouch protocol.
* EV_KEY:
-Used to describe state changes of keyboards, buttons, or other key-like
Devices.
* EV_MSC:
-Used to describe miscellaneous input data that do not fit into other types.。
对于EV_SYN其中CODE是不同的键值。
图中是输入了一个Z键,一秒后抬起的总输出信息,它们的意思分别是:
TYPE | CODE | VALUE | 备注 |
EV_SYN | SYN_REPORT | 0 | 上报 |
1 | Auto-repeat | ||
EV_KEY | KEY_NAME | DOWN | 按下 |
UP | 抬起 | ||
REPEAT | Kernel soft-repeat | ||
EV_MSC | MSC_SCAN | 0007001D | 键盘HID码《》 |
对于Auto-repeat的实现,内核中有自行实现,但是在上层系统中真正使用的不多,比如x11,Android。像Android是这样说的:来源《keyboard-devices》
Android performsits own keyboard repeating. Auto-repeat functionality should be disabled in thedriver.
如果需要禁用kernel中的auto-repeat以减小上层系统的无用处理,可以使用以下方法:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#ifndef EVIOCSREP
#define EVIOCSREP _IOW('E', 0x03, int[2])
#endif
int main(int argc, char** argv) {
int fd = 0;
int rep[2] = {0};
if ((fd = open("/dev/input/event4",
O_RDWR))< 0) {
perror("unableto access /dev/input/event2, exiting");
exit(1);
}
// get current auto-repeat args.
if (ioctl(fd,EVIOCGREP, rep)) {
perror("unableto set delay and repeat rate for input devices");
exit(1);
}
rep[1] =0; // set auto-repeat rate as 0.
if (ioctl(fd,EVIOCSREP, rep)) {
perror("unableto set delay and repeat rate for input devices");
exit(1);
}
printf("rep[0]:%d;rep[1]:%d\n", rep[0], rep[1]);
close(fd);
}
《完》
注1:当然也可以在 优快云上下载