这节,我们来说下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 <linux/input.h>
在这个文件中,我们可以找到这个结构体:
描述一个输入事件 /* * 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 <stdio.h> #include <linux/input.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> /* 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!\n"); return -1 ; } while(1){ //3、读事件 read(fd ,&event_mouse ,sizeof(event_mouse)); //4、判断事件类型,并打印键码 switch(event_mouse.type){ case EV_SYN: printf("sync!\n"); 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\n",event_mouse.code); printf("event_mouse.value_X:%d\n",event_mouse.value); } if(event_mouse.code == REL_Y){ printf("event_mouse.code_Y:%d\n",event_mouse.code); printf("event_mouse.value_Y:%d\n",event_mouse.value); } defalut: break ; } } return 0 ; }
编译完后,执行,当移动鼠标的时候我们就可以看到以下信息:
说明,此时已经读到鼠标事件所反应出来的值了。
那么,既然可以做读的操作,那肯定也就可以写了,可不可以写一个程序,让鼠标画个正方形,或者让它自动按着自己
的路径去移动?答案是肯定可以的,要不然游戏外挂都是怎么做出来的?大家说是吧,下面我们就让鼠标来自己画一个
正方形,上代码:
#include <stdio.h> #include <linux/input.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> /* 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!\n"); 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驱动。