这节,我们来说下input子系统,什么是input子系统?input子系统就是输入子系统。 输入子系统是 Linux内核用于管理各种输入设备 (键盘,鼠标,遥控杆,书写板等等 )的部分,用户通过输入子系统进行内核,命令行,图形接口之间的交换。输入子系统在内核里实现,因为设备经常要通过特定的硬件接口被访问 (例如串口, ps/2, usb等等 ),这些硬件接口由内核保护和管理。内核给用户导出一套固定的硬件无关的 input API,供用户空间程序使用。
输入子系统分为三块: input core, drivers和 event handlers。正常的路径是从底层硬件到驱动,从驱动到 input core,从 input core到 event handler,从 event handler到 user space。在应用程序的角度,我们只要关心user space就行了其余的部分我们不用关心。 我们先从应用程序的角度来认识下input子系统,我们可以从这个文件可以看到对应的设备
/proc/bus/input/devices
I: Bus=0003 Vendor=046d Product=c018 Version=0111
N: Name=" USB Optical Mouse"
P: Phys=usb-0000:00:1d.1-2/input0
S: Sysfs=/class/input/input24
U: Uniq=
H: Handlers=mouse1 event2
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
在Linux系统下cat /
/proc/bus/input/devices即可看到以上相关的信息,这些devices主要是用来描述注册在input子系统的一些设备文件,可能有鼠标,键盘,触摸屏,重力传感器,温度传感器等等的设备。 那我们如何来描述这样的一个设备呢?Linux系统为我们提供了以下这个头文件: #include
在这个文件中,我们可以找到这个结构体:描述一个输入事件
/*
* The event structure itself
*/
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
先来解读下这个结构体的含义:struct timeval结构体在time.h中的定义为:
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /*Microseconds. */
};
其中,tv_sec为Epoch到创建struct timeval时的秒数,tv_usec为微秒数,即秒后面的零头。type域显示了被报告事件的类型,例如,一个 key press或者 button press, relative motion(比如移动鼠标 )或者 absolute motion(比如移动游戏杆 ); code域告诉你是哪一个key或者坐标轴在被操作; value域告诉你现在的状态或者运动情况是什么。
那么,最主要的事件有以下三种:相对事件(鼠标),绝对事件(触摸屏),键盘事件。例如:我们说说鼠标,我们在移动鼠标的时候鼠标就是一个相对事件,所以type的类型也就是底层上报给用户的事件为相对事件类型,那么code表示的就是相对于鼠标当前的位置的X或者Y的坐标,value也就是相对于当前的位置偏移多少。
事件类型(type)在input.h主要有以下:/*
* Event types
*/
#define EV_SYN 0x00 //同步事件,就是将结果上报给系统的过程
#define EV_KEY 0x01 //按键事件
#define EV_REL 0x02 //相对事件
#define EV_ABS 0x03 //绝对事件
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
我们这节主要来写写鼠标的事件读取还有控制,所以我们找到鼠标相关的code:/*
* Relative axes
*/
//在这里,我们暂时只会用来REL_X和REL_Y这两个参数
#define REL_X 0x00 //相对X坐标
#define REL_Y 0x01 //相对Y坐标
#define REL_Z 0x02
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
那么value,就是选择具体的type,具体的code以后所反应出来的值,鼠标就是相对于当前X或者相对于当前Y的值。接下来,我们来看一下如何来读取鼠标事件,写一段代码测试一下:#include
#include
#include
#include
#include
/*
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
*/
/*
Event types
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
*/
/*
Relative axes
#define REL_X 0x00
#define REL_Y 0x01
#define REL_Z 0x02
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
*/
//event8 mouse
//event9 keyboard
int main(void)
{
//1、定义一个结构体变量用来描述input事件
struct input_event event_mouse ;
//2、打开input设备的事件节点 我的电脑鼠标事件的节点是event3
int fd = open("/dev/input/event3",O_RDWR);
int value ;
int type ;
int buffer[10]={0};
if(-1 == fd){
printf("open mouse event fair!
");
return -1 ;
}
while(1){
//3、读事件
read(fd ,&event_mouse ,sizeof(event_mouse));
//4、判断事件类型,并打印键码
switch(event_mouse.type){
case EV_SYN:
printf("sync!
");
break ;
case EV_REL:
//鼠标事件,XY相对位移
//code表示相对位移X或者Y,当判断是X时,打印X的相对位移value
//当判断是Y时,打印Y的相对位移value
if(event_mouse.code == REL_X){
printf("event_mouse.code_X:%d
",event_mouse.code);
printf("event_mouse.value_X:%d
",event_mouse.value);
}
if(event_mouse.code == REL_Y){
printf("event_mouse.code_Y:%d
",event_mouse.code);
printf("event_mouse.value_Y:%d
",event_mouse.value);
}
defalut:
break ;
}
}
return 0 ;
}
编译完后,执行,当移动鼠标的时候我们就可以看到以下信息:说明,此时已经读到鼠标事件所反应出来的值了。那么,既然可以做读的操作,那肯定也就可以写了,可不可以写一个程序,让鼠标画个正方形,或者让它自动按着自己的路径去移动?答案是肯定可以的,要不然游戏外挂都是怎么做出来的?大家说是吧,下面我们就让鼠标来自己画一个正方形,上代码:#include
#include
#include
#include
#include
/*
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
*/
/*
Event types
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
*/
/*
Relative axes
#define REL_X 0x00
#define REL_Y 0x01
#define REL_Z 0x02
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX+1)
*/
//event8 mouse
//event9 keyboard
int main(void)
{
//1、定义一个结构体变量用来描述input事件
struct input_event event_mouse ;
//2、打开input设备的事件节点 我的电脑鼠标事件的节点是event3
int fd = open("/dev/input/event3",O_RDWR);
int value ;
int type ;
int i ;
int buffer[10]={0};
if(-1 == fd){
printf("open mouse event fair!
");
return -1 ;
}
while(1){
//3、写事件
for(i = 0 ; i < 20 ; i++){
event_mouse.type = EV_REL ;
event_mouse.code = REL_X ;
event_mouse.value = i ;
write(fd,&event_mouse,sizeof(event_mouse));
event_mouse.code = 0 ;
event_mouse.value = 0;
event_mouse.type = EV_SYN ;
write(fd,&event_mouse,sizeof(event_mouse));
usleep(50000);
}
for(i = 0 ; i < 20 ; i++){
event_mouse.type = EV_REL ;
event_mouse.code = REL_Y ;
event_mouse.value = i ;
write(fd,&event_mouse,sizeof(event_mouse));
event_mouse.code = 0 ;
event_mouse.value = 0 ;
event_mouse.type = EV_SYN ;
write(fd,&event_mouse,sizeof(event_mouse));
usleep(50000);
}
for(i = 0 ; i > -20 ; i--){
event_mouse.type = EV_REL ;
event_mouse.code = REL_X ;
event_mouse.value = i ;
write(fd,&event_mouse,sizeof(event_mouse));
event_mouse.code = 0 ;
event_mouse.value = 0;
event_mouse.type = EV_SYN ;
write(fd,&event_mouse,sizeof(event_mouse));
usleep(50000);
}
for(i = 0 ; i > -20 ; i--){
event_mouse.type = EV_REL ;
event_mouse.code = REL_Y ;
event_mouse.value = i ;
write(fd,&event_mouse,sizeof(event_mouse));
event_mouse.code = 0 ;
event_mouse.value = 0 ;
event_mouse.type = EV_SYN ;
write(fd,&event_mouse,sizeof(event_mouse));
usleep(50000);
}
}
return 0 ;
}
执行结果:此时鼠标是在移动的,由于以下的图片是禁止的,效果留给大家自己去尝试。
下节,我们将来实现一下input驱动。