常见的输入设备包括鼠标、键盘、触摸屏、按钮等,它们都能产生输入事件,产生输入数据给计算机系统。
Linux 系统为了统一管理输入设备,实现了一套能够兼容所有输入设备的框架,这个框架就是input 子系统。驱动开发人员基于input 子系统开发输入设备的驱动程序,input 子系统可以屏蔽硬件的差异,向应用层提供一套统一的接口。
基于input 子系统注册成功的输入设备,都会在/dev/input 目录下生成对应的设备节点(设备文件),设备节点名称通常为eventX( X 表示一个数字编号0、1、2、3 等),例如/dev/input/event0, /dev/input/event1,/dev/input/event2 等,通过读取这些设备节点可以获取输入设备上报的数据。
cat /proc/bus/input/devices
如果我们要读取触摸屏的数据,假设触摸屏设备对应的设备节点为/dev/input/event0 , 那么数据读取流程如下:
应用程序打开输入设备对应的设备文件,向其发起读操作,那么这个读操作获取到的数据是什么样的数据呢?其实每一次read 操作获取的都是一个struct input_event 结构体类型数据,该结构体定义在
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
type 用于描述发生了哪一种类型的事件(对事件的分类),Linux 系统所支持的输入事件类型如下:
类型 | 代码中的值 | 描述 |
---|---|---|
EV_SYN | 0x00 | 同步类事件,用于同步事件 |
EV_KEY | 0x01 | 按键类事件 |
EV_REL | 0x02 | 相对位移类事件(例如鼠标) |
EV_ABS | 0x03 | 绝对位移类事件(例如触摸屏) |
EV_MSC | 0x04 | 其他杂类事件 |
EV_SW | 0x05 | |
EV_LED | 0x06 | |
EV_SND | 0x07 | |
EV_REP | 0x08 | |
EV_FF | 0x09 | |
EV_PWR | 0x0A | |
EV_FF_STATUS | 0x0B | |
EV_MAX | 0x1F | |
EV_CNT | (EV_MAX+1) |
一种输入设备通常可以产生多种不同类型的事件。例如点击鼠标按键(左键,右键)时会上报按键类事件,移动鼠标时,则会上报位移类事件。
同步事件(EV_SYN)用于实现同步操作、告知接收者本轮上报的数据已经完整。应用程序读取输入设备上报的数据时,一次read 操作只能读取一个struct input_event 类型数据。例如对于触摸屏来说,一个触摸点的信息包含了X 坐标、Y坐标以及其他信息,对于这样的情况,应用程序需要执行多次read 操作才能把一个触摸点的信息全部读取出来,这样才能得到触摸点的完整信息。
那么应用程序如何得知本轮已经读取到完整的数据呢?其实这就是通过同步事件来实现的,内核将本轮需要上报、发送给接收者的数据全部上报完毕后,接着会上报一个同步事件,以告知应用程序本轮数据已经完整、可以进行同步了。
同步事件 | 代码中的值 | 描述 |
---|---|---|
SYN_REPORT | 0 | 数据上报 |
SYN_CONFIG | 1 | |
SYN_MT_REPORT | 2 | |
SYN_DROPPED | 3 | |
SYN_MAX | 4 | 最大同步事件 |
SYN_CNT | 5 | 同步事件的数量 |
表示该类事件中的哪一个具体事件,每一种事件类型都包含多种不同的事件。
按键类事件 | 代码中的值 | 描述 |
---|---|---|
KEY_RESERVED | 0 | 保留 |
KEY_ESC | 1 | ESC 键 |
KEY_1 | 2 | 数字键1 |
KEY_2 | 3 | 数字键2 |
KEY_TAB | 15 | TAB键 |
KEY_Q | 16 | 字母Q |
KEY_W | 17 | 字母W |
相对位移事件code | 代码中的值 | 描述 |
---|---|---|
REL_X | 0x00 | X 轴 |
REL_Y | 0x01 | Y轴 |
REL_Z | 0x02 | Z 轴 |
REL_RX | 0x03 | |
REL_RY | 0x04 | |
REL_RZ | 0x05 |
触摸屏设备是一种绝对位移设备,它能够产生绝对位移事件。例如对于触摸屏来说,一个触摸点所包含的信息可能有很多种,例如触摸点的x轴坐标,Y轴坐标,按压力大小以及触控面积等。所以code 变量告知应用程序,当前上报的是触摸点的哪一种信息(x 坐标还是Y轴坐标,亦或其它)。
绝对位移事件code | 代码中的值 | 描述 |
---|---|---|
ABS_X | 0x00 | X 轴 |
ABS_Y | 0x01 | Y轴 |
ABS_Z | 0x02 | Z 轴 |
ABS_RX | 0x03 | |
ABS_RY | 0x04 | |
ABS_RZ | 0x05 |
内核每次上报事件都会向应用层发送一个数据value。对value 值的解释随着code 的变化而变化。
例如对于按键事件(type = 1)来说,如果code = 2 (键盘上的数字键1,也就是KEY_1),那么如果value 等于1,则表示KEY_1键按下;value 等于0表示KEY_1 键松开;如果value 等于2,则表示KEY_1 键长按。
例如在绝对位移事件中(type = 3),如果code = 0 (触摸点x 坐标ABS_X),那么value 值就等于触摸点的X 轴坐标值;同理,如果code = 1 (触摸点Y轴坐标ABS_Y),此时value 值便等于触摸点的Y轴坐标值。
type | code | value | 说明 |
---|---|---|---|
1 | 2 | 1 | 键盘上的数字键1,按下 |
1 | 2 | 0 | 键盘上的数字键1,松开 |
1 | 2 | 2 | 键盘上的数字键1,长按 |
3 | 0 | val | 触摸点X轴坐标:val |
3 | 1 | val | 触摸点Y轴坐标:val |
单点触摸设备只支持单点触摸,一轮完整的数据值包含一个触摸点信息。单点触摸设备以ABS_XXX 事件承载、上报触摸点信息。
多点触摸设备可支持多点触摸,一轮完整的数据可能包含多个触摸点的信息。多点触摸设备则是以ABS_MT_XXX 事件承载、上报触摸点的信息。
触摸屏设备除了上报绝对位移事件之外,还可以上报按键类事件和同步类事件。
在Linux 内核中,多点触摸设备使用多点触摸(MT)协议上报各个触摸点的数据,MT协议分为两种类型:Type A 和Type B。Type A 协议实际使用中用得比较少,几乎处于淘汰的边缘。
能够追踪并区分触摸点的设备通常在硬件上能够区分不同的触摸点,例如对于5点触摸设备来说,硬件能够为每个识别到的触摸点与一个slot 进行关联,这个slot 就是一个编号,用来区分触摸点0,触摸点1,触摸点2.
Type B协议适用于能够追踪并区分触摸点的设备,协议重点是通过ABS_MT_SLOT 事件上报各个触摸点信息的更新。ABS_MT_SLOT 事件中对应的value 数据存放的便是一个slot,以告知应用层当前正在更新的slot 关联的触摸点对应的信息。
ABS_MT_TRACTKING_TD 事件用于触摸点的创建、替换和销毁工作,它携带的value 表示一个ID,一个非负数的ID表示一个有效的触摸点,如果ID等于-1 表示该触摸点已经不存在、被移除了;一个以前不存在的ID 表示这是一个新的触摸点。
Type B 协议可以减少发送到用户空间的数据,只发生了变更的数据才会上报,例如某个触摸点发生了移动,但仅仅只改变了X 轴坐标、而未改变Y轴坐标,那么内核只会将改变后的X 坐标值通过ABS_MT_POSITION_X 事件发送给应用层。