Linux 下Input系统应用编程实战
作者:杨源鑫(也是我们的校园代理)
经授权转载于公众号嵌入式开发圈,有些许修改。
什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘、鼠标,智能手机上的触摸屏,按键。都是输入设备。那么操作系统怎么管理这些输入设备?这里以最常用的Linux操作系统进行讲解。
在Linux内核中,有非常多用于管理诸多设备的子系统,比如显示系统,输入子系统,音频子系统,电源管理子系统,时钟管理子系统等等,本节我们重点关注输入子系统。
输入子系统是在内核里实现,因为设备经常要通过特定的硬件接口被访问 (例如串口, ps/2, usb等等 ),这些硬件接口由内核保护和管理。内核给用户导出一套固定的与硬件无关的 input API,供用户空间程序使用。
在Linux input子系统中,分三块进行管理,分别是: input core(输入系统核心层), drivers(输入系统驱动层)和 event handlers(输入系统事件层),可能你感觉太抽象,看下图4-5-9就清楚了。
先从应用程序角度认识input子系统,我们可以从以下这个文件看到对应的设备。
打开Linux终端,输入命令cat /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
这些devices主要是用来描述注册在input子系统的设备文件,可能有鼠标,键盘,触摸屏,重力传感器,温度传感器等等,写驱动的时候,通过内核提供的input设备注册设备相关的接口后,这些信息都会保存到对应的文件里。
那么,input子系统如何描述输入设备呢?
Linux系统为我们提供了这个输入系统操作相关的头文件:
include <linux/input.h>
在这个文件中,可以找到这个结构体:
//用于描述一个输入事件
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
在这里我们看到input_event结构体中还嵌套了另一个结构体struct timeval time;
先解读struct timeval time,它在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 //绝对事件
本节,我们来实现一个input控制鼠标的应用程序。所以还会用到以下事件:
/*
* Relative axes
*/
//在这里,我们暂时只会用REL_X和REL_Y这两个参数
#define REL_X 0x00 //相对X坐标
#define REL_Y 0x01 //相对Y坐标
我们可以使用cat命令来测试当前的鼠标事件到底属于哪一个事件节点,如图4-5-10所示:
只需切换到/dev/input,找到对应的事件节点,再使用cat eventx(事件节点),然后移动鼠标就可以看到数据打印啦,但是这些数据我们显然是看不懂的,不过可以使用测试程序将鼠标的值读出来。
接下来,我们写个程序mouse.c来看看如何读取鼠标事件,
#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_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
//读者的电脑的设备节点可能和我的不一样,可以使用cat命令去获取,然后
//不断尝试
int fd = open("/dev/input/event4",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 ;
}
运行结果,如图4-5-11所示。
当我们不断移动鼠标的时候,这些值将会被打印出来。
请思考一个问题,既然我们移动鼠标能够打印数值,那能不能够写一个程序控制鼠标自动移动呢?肯定可以,下面我们写个程序让鼠标自己画一个正方形,上代码:
#include <stdio.h>
#include <linux/input.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
//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 ;
}
执行效果请读者自行验证。
接下来我们再写一个案例:在Tiny4412平台上获取电容屏的坐标值。
触摸屏上报坐标值的事件属于绝对事件,也就是,触摸的坐标点X和Y会在屏幕的分辨率范围内上报一个绝对的坐标(X,Y)。
那么上报对于的类型(type)如下:EV_ABS
对于的code如下:
绝对于X:
ABS_MT_POSITION_X
绝对于Y:
ABS_MT_POSITION_Y
我用了一个程序获取了屏幕的分辨率,得知分辨率宽为480,高为800。
首先,写这个程序时,我通过adb进到Android根目录,然后用getevent -p查到触摸屏的事件节点为event0, 同时也知道触摸屏是一个绝对事件,如下:
接下来,我在Android5.0的源代码external目录下创建了如下目录:Getft5x0x_Test
该目录下有如下两个文件文件:
Android.mk和Get_ft5x0x_tp.c
(1) 先看Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SHARED_LIBRARIES += libcutils libutils
#LOCAL_STATIC_LIBRARIES += libz libstdc++ libpng libvtpng
LOCAL_STATIC_LIBRARIES += libz libstdc++ libpng
LOCAL_SRC_FILES := Get_ft5x0x_tp.c
LOCAL_MODULE := ft5x0x_tp
include $(BUILD_EXECUTABLE)
(2)Get_ft5x0x_tp.c
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include <inttypes.h>
#include <errno.h>
//ft5x0x_ts触摸屏事件初始化
18int touch_fd = -1 ;
19int ft5x0x_ts__init(void)
{
touch_fd = open("/dev/input/event0", O_RDONLY);
if (touch_fd < 0)
{
printf("open /dev/input/event0 failed\n");
return -1;
}
return 0;
}
//获取ft5x0x_ts触摸屏上的坐标点
int Get_ft5x0x_ts_postion(int *x, int *y)
{
int touch_ret = -1 ;
//1、定义一个结构体变量用来描述ft5x0x触摸屏事件
struct input_event ft5x0x_ts ;
//2、读事件
touch_ret = read(touch_fd ,&ft5x0x_ts ,sizeof(ft5x0x_ts));
if(touch_ret < 0){
printf("read touch fair!\n");
}
//3、判断事件类型
switch(ft5x0x_ts.type)
{
case EV_SYN:
break ;
case EV_ABS:
if(ft5x0x_ts.code == ABS_MT_POSITION_X){
*x = ft5x0x_ts.value ;
}
if(ft5x0x_ts.code == ABS_MT_POSITION_Y){
*y = ft5x0x_ts.value ;
}
defalut:
break ;
}
return 0;
}
int main(int argc, char **argv)
{
int tp_ret ;
int ft5x0x_x = 0;
int ft5x0x_y = 0;
tp_ret = ft5x0x_ts__init();
if(-1 == tp_ret){
printf("tp init fair!\n");
return -1 ;
}
printf("tp init success!\n");
while(1)
{ //获取屏幕上的绝对坐标点
Get_ft5x0x_ts_postion(&ft5x0x_x,&ft5x0x_y);
printf("ft5x0x_x:%d ft5x0x_y:%d\n",ft5x0x_x,ft5x0x_y);
usleep(100);
}
return 0;
}
编写完Android.mk和C程序后,切换到Android的根目录,用以下命令编译Get_ft5x0x_tp.c
(使用mmm命令之前一定要先执行source和lunch这两个步骤)
root@morixinguan:/work/android-5.0.2# source build/envsetup.sh
including device/samsung/manta/vendorsetup.sh
including device/moto/shamu/vendorsetup.sh
including device/friendly-arm/tiny4412/vendorsetup.sh
including device/generic/mini-emulator-x86_64/vendorsetup.sh
including device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh
including device/generic/mini-emulator-mips/vendorsetup.sh
including device/generic/mini-emulator-arm64/vendorsetup.sh
including device/generic/mini-emulator-x86/vendorsetup.sh
including device/asus/deb/vendorsetup.sh
including device/asus/fugu/vendorsetup.sh
including device/asus/grouper/vendorsetup.sh
including device/asus/tilapia/vendorsetup.sh
including device/asus/flo/vendorsetup.sh
including device/lge/hammerhead/vendorsetup.sh
including device/lge/mako/vendorsetup.sh
including sdk/bash_completion/adb.bash
root@morixinguan:/work/android-5.0.2# lunch
You're building on Linux
Lunch menu... pick a combo:
. aosp_arm-eng
. aosp_arm64-eng
. aosp_mips-eng
. aosp_mips64-eng
. aosp_x86-eng
. aosp_x86_64-eng
. aosp_manta-userdebug
. aosp_shamu-userdebug
. full_tiny4412-userdebug
. full_tiny4412-eng
. mini_emulator_x86_64-userdebug
. m_e_arm-userdebug
. mini_emulator_mips-userdebug
. mini_emulator_arm64-userdebug
. mini_emulator_x86-userdebug
. aosp_deb-userdebug
. full_fugu-userdebug
. aosp_fugu-userdebug
. aosp_grouper-userdebug
. aosp_tilapia-userdebug
. aosp_flo-userdebug
. aosp_hammerhead-userdebug
. aosp_mako-userdebug
Which would you like? [aosp_arm-eng] 20
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=5.0.2
TARGET_PRODUCT=aosp_tilapia
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=cortex-a9
TARGET_2ND_ARCH=
TARGET_2ND_ARCH_VARIANT=
TARGET_2ND_CPU_VARIANT=
HOST_ARCH=x86_64
HOST_OS=linux
HOST_OS_EXTRA=Linux-4.8.0-46-generic-x86_64-with-Ubuntu-16.04-xenial
HOST_BUILD_TYPE=release
BUILD_ID=LRX22G
OUT_DIR=out
====================
root@morixinguan:/work/android-5.0.2#
接下来,编译程序 :
mmm external/Getft5x0x_Test/
然后我们看到以下显示:
这个二进制生成的绝对路径是out目录, 我们需要ft5x0x_tp文件,这个名字就是上面Android.mk里面对应的:
LOCAL_MODULE := ft5x0x_tp
Install: out/target/product/tiny4412/system/bin/ft5x0x_tp
将这个文件拷贝到当前目录:
cp out/target/product/tiny4412/system/bin/ft5x0x_tp .
然后,用USB线连接tiny4412开发板,再用adb命令将ft5x0x_tp push到system/bin/目录下,这个目录是Android的根文件系统下的一个命令,很多命令都在这个目录下。
(若没有安装adb,可以apt-get install adb 进行安装)
adb push完毕以后,再adb shell切换到根目录下
执行ft5x0x_tp.bin,然后触摸触摸屏,这样,坐标值就打印出来了。
ps:韦东山2期视频对输入子系统的驱动编写以及机制讲解的很清楚,有需要的可以看看
--END--
关注公众号百问科技(ID:baiwenkeji)第一时间阅读嵌入式干货。
技术交流加个人威信13266630429,验证:博客园
Linux 下Input系统应用编程实战的更多相关文章
- Linux下librtmp使用及编程实战
最近想做rtmp的推流.直播的小项目,不想直接使用FFmpeg进行推流,FFmpeg进行推流特别简单,因为它已经将编码以及librtmp都集成好了,没啥意思.FFmpeg推流的例子,在雷神的博客里可以 ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Linux下的C Socket编程 -- 简介与client端的处理
Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...
- Linux下高并发网络编程
Linux下高并发网络编程 1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打 ...
- linux下C语言多线程编程实例
用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...
- Linux/Android——Input系统之InputMapper 处理 (八)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/43561773 前文Linux/Android——Input系统之InputReader (七)介 ...
- Linux/Android——Input系统之InputReader (七)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/42739197 在前文Linux/Android——Input系统之frameworks层Inpu ...
- Linux/Android——Input系统之frameworks层InputManagerService (六)【转】
本文转载自:http://blog.csdn.net/u013491946/article/details/72638954 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为 ...
- Linux下搭建C/C++编程环境
Linux下搭建C/C++编程环境 1.KDevelop下载 wget -O KDevelop.AppImage https://download.kde.org/stable/kdevelop/5. ...
随机推荐
- Java第三次实验要求
实验三 类与对象(一) 一. 实验目的 1. 掌握类与对象的基本概念: 2. 掌握类的声明.创建与用法: 3. 掌握类的构造方法的定义与用法 4. 掌握类的成员变量.成员方法的定义与用法: 5. 理解 ...
- 求助大神!怎样除去XML节点反复的值的数据
<?xml version="1.0" encoding="utf-8"? > <UpdCfg> <Upgrade> < ...
- vue2.0 自定义 下拉刷新和上拉加载更多(Scroller) 组件
1.下拉刷新和上拉加载更多组件 Scroller.vue <!-- 下拉刷新 上拉加载更多 组件 --> <template> <div :style="mar ...
- python(36)- 测试题
1.8<<2等于? 32 “<<”位运算 264 132 64 32 16 8 4 2 1 原始位置 0 0 0 0 0 1 0 0 0 想左位移2位 0 0 0 1 0 0 ...
- vs2012编译ffmpeg
从官方网站down下来的ffmpeg没有pdb文件不方便调试,为此使用VS2012编译ffmpeg. 编译步骤: 一.安装MinGW,具体的安装方法上一篇文章已经有介绍这里不在赘述. 二.下载文件并放 ...
- Hadoop学习笔记(一)——Hadoop体系结构
HDFS和MapReduce是Hadoop的两大核心. 整个Hadoop体系结构主要是通过HDFS来实现分布式存储的底层支持的,而且通过MapReduce来实现分布式并行任务处理的程序支持. 一.HD ...
- 在jquery的ajax方法中的success中使用return要注意的问题
jquery的ajax方法:在success中使用return:来结束程序的时候,结束的只是success这个方法,也就是说success中的return的作用范围只是success: 如果要想在su ...
- [IT新应用]如何拯救死机的苹果手机(iPhone X)
突然白天接了一个电话,苹果就死机了.这是用这个手机半年来第一次.貌似还能接电话,就是屏幕上一个白色的圆圈,一直转啊转. 后来百度了一下,找到这一篇.将重点部分摘录如下: http://www.sohu ...
- 安装Ubuntn 和 pycharm
Ubuntu安装之python开发 什么??公司要用Ubuntu(乌班图)?不会用??怎么进行python开发??? 乌班图操作系统下载地址:http://releases.ubuntu.com/ ...
- iOS不用上架就能下载安装ipa应用内测:使用FIR.im发布自己的移动端APP
本文转自:http://www.cnblogs.com/imzzk/p/firim.html 一次很偶然的机会知道fir.im,这家公司主要的产品就是帮助开发者方便便捷地发布iOS或者Android应 ...