输入设备包括:键盘、鼠标、按键、触摸屏等。
在linux系统中为了处理输入设备上报的信息,统一管理这些设备,实现了一套可以兼容各种设备的驱动框架就是INPUT子系统。
通过INPUT子系统管理的设备,通常都会在/dev/input目录下创建对应的设备节点,以供上层应用进行读取。
一、读取的数据类型
对于INPUT子系统管理的设备,每次读取的数据都是一个struct input_event 结构体类型数据,其定义在<linux/input.h>头文件中:
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
time:内核记录的上报该数据的时间
type:用于描述发生的是哪一种事件类型(比如按键事件)
code:描述的是该类事件中的一个具体事件(比如按键事件中按的是KEY_0按键)
value: 内核每次上报事件都会向应用层发送一个数据 value, 对 value 值的解释随着 code 的变化而
变化。
具体的关于INPUT子系统数据的定义可以在头文件input-event-codes.h中进行查找。
二、通过代码读取struct input_event 数据
读取按键上报的数据
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
struct input_event event;
int fd;
if(argc != 2) { //命令行输入参数个数不等于2,返回错误
fprintf(stderr, "usage: %s <input-errpr>\n", argv[0]);
exit(-1);
}
fd = open(argv[1], O_RDONLY);
if(fd < 0) {
perror("open failed \n");
exit(-1);
}
for ( ; ; ) {
/* 循环读取数据 */
if (sizeof(struct input_event) !=
read(fd, &event, sizeof(struct input_event))) {
perror("read error");
exit(-1);
}
printf("type:%d code:%d value:%d\n",
event.type, event.code, event.value);
}
exit(0);
}
程序流程很简单,循环读取按键对应的设备节点上传的数据信息,并进行打印,测试效果如下:
第一行中 type 等于 1,表示上报的是按键事件 EV_KEY, code=114, 打开 input-event-codes.h 头文件进行查找,可以发现 cpde=114 对应的是键盘上的 KEY_VOLUMEDOWN 按键,而 value=1 表示按键按下
第二行, 表示上报了 EV_SYN 同步类事件(type=0)中的 SYN_REPORT 事件(code=0), 表示本轮数据已经完整、报告同步。
第三行, type 等于 1,表示按键类事件, code 等于 114、value 等于 0,所以表示按键 KEY_VOLUMEDOWN被松开。
第四行,又上报了同步事件
读取触摸屏上报的数据
第一行中type = 3表示是绝对位移事件EV_ABS,code = 57表示是一个ABS_MT_TRACKING_ID,value = 51表示这是第51个创建的点
第二行中type = 3表示是绝对位移事件EV_ABS,code = 53 表示ABS_MT_POSITION_X,value = 309表示是x的坐标。
同理第三行是y的坐标。
第四行type = 1表示EV_KEY按键类事件,code = 330表示是一个BIN_TOUCH触摸事件,value = 1表示按下。
第五行和第六行分别上报了绝对位移事件 EV_ABS(type=3)中的 ABS_X(code=0)和 ABS_Y(code=1),其 value 分别对应的是触摸点的 X 坐标和 Y 坐标。 多点触摸设备也会通过 ABS_X、 ABS_Y 事件上报触摸点的 X、 Y 坐标。
最后一行上报了同步类事件 EV_SYN(type=0)中的 SYN_REPORT(code=0)事件,表示此次触摸点的信息全部上报完毕。
三、触摸屏单点触摸程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
struct key_struct {
int touch_x; //保存触摸点x
int touch_y; //保存触摸点y
int down; //触摸状态 1:按下 0:松开 -1:移动
int valid; //判断数据是否有效 1:有效 0:无效
};
int main(int argc, char *argv[])
{
struct input_event event;
struct key_struct touch;
int fd;
/*检验命令行参数是否正确*/
if(argc != 2) {
fprintf(stderr, "error used!\n");
exit(-1);
}
/*打开文件*/
fd = open(argv[1], O_RDONLY);
if(fd < 0) {
perror("open failed!\n");
exit(-1);
}
/*初始化触摸状态*/
touch.touch_x = 0;
touch.touch_y = 0;
touch.down = -1;
touch.valid = 0;
while(1) {
/*循环读取数据*/
if (read(fd, &event, sizeof(struct input_event)) !=
sizeof(struct input_event)) {
perror("read error");
exit(EXIT_FAILURE);
}
switch(event.type) {
case EV_KEY : //按键事件
if(event.code = BTN_TOUCH) {
touch.down = event.value; //保存按键事件的数值
touch.valid = 1;
}
break;
case EV_ABS : //绝对位移事件
switch(event.code) {
case ABS_X : //绝对位移X坐标
touch.touch_x = event.value; //保存X坐标
touch.valid = 1;
break;
case ABS_Y : //绝对位移Y坐标
touch.touch_y = event.value; //保存Y坐标
touch.valid = 1;
break;
}
break;
case EV_SYN : //同步事件
if(event.value == SYN_REPORT) {
if(touch.valid) { //判断按键是否有效
switch(touch.down) {
case 1 : //按下按键
printf("按下%d, %d\n",touch.touch_x,touch.touch_y);
break;
case 0:
printf("松开触摸\n");
break;
case -1:
printf("移动%d, %d\n",touch.touch_x,touch.touch_y);
break;
}
touch.valid = 0;
touch.down = -1;
}
}
break;
}
}
exit(0);
}
代码流程:
① 创建一个结构体其中touch_x表示x点的坐标,touch_y表示y点的坐标,down表示按键的状态,valid表示本次按键是否是有效的。
②变量 down 表示手指状态时候按下、松开还是滑动, down=1 表示手指按下、 down=0 表示手指松开、down=-1 表示手指滑动;
③变量 valid 表示数据是否有效, valid=1 表示有效、 valid=0 表示无效;有效指的是我们检测的信息发生了更改,譬如程序中只检测了手指的按下、松开动作以及坐标值的变化。
④调用open函数打开触摸屏对线的设备节点,并在死循环中反复读取input子系统上报的数据。
测试效果如下: