Linux 下Input系統(tǒng)應(yīng)用編程實(shí)戰(zhàn)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
微信公眾號(hào):morixinguan
關(guān)注可了解更多的教程。問(wèn)題或建議,請(qǐng)公眾號(hào)留言;
如果你覺(jué)得本文對(duì)你有幫助,歡迎贊賞
▲長(zhǎng)按圖片保存可分享至朋友圈
什么是input子系統(tǒng)?不管是什么操作系統(tǒng),都有一個(gè)程序是用于管理各種輸入設(shè)備的,打個(gè)比方,生活中使用的電腦鍵盤(pán)、鼠標(biāo)就是輸入設(shè)備,小到日常生活中無(wú)可避免的智能手機(jī),智能手機(jī)上的觸摸屏,按鍵也是輸入設(shè)備。那么操作系統(tǒng)是怎么管理這些輸入設(shè)備的呢?這里還是以最常用的操作系統(tǒng)Linux來(lái)進(jìn)行講解。
在Linux內(nèi)核中,有非常多的子系統(tǒng),用于管理很多設(shè)備,比如顯示系統(tǒng),輸入子系統(tǒng),音頻子系統(tǒng),電源管理子系統(tǒng),時(shí)鐘管理子系統(tǒng)等等,本節(jié)我們重點(diǎn)關(guān)注Linux下的輸入子系統(tǒng)。輸入子系統(tǒng)是 Linux內(nèi)核用于管理各種輸入設(shè)備 (鍵盤(pán),鼠標(biāo),遙控桿,書(shū)寫(xiě)板等等 )的部分,用戶通過(guò)輸入子系統(tǒng)進(jìn)行內(nèi)核,命令行,圖形接口之間的交換。
輸入子系統(tǒng)在內(nèi)核里實(shí)現(xiàn),因?yàn)樵O(shè)備經(jīng)常要通過(guò)特定的硬件接口被訪問(wèn) (例如串口, ps/2, usb等等 ),這些硬件接口由內(nèi)核保護(hù)和管理。內(nèi)核給用戶導(dǎo)出一套固定的硬件無(wú)關(guān)的 input API,供用戶空間程序使用。
在Linux的輸入子系統(tǒng)中,分三塊進(jìn)行管理,分別是: input core(輸入系統(tǒng)核心層), drivers(輸入系統(tǒng)驅(qū)動(dòng)層)和 event handlers(輸入系統(tǒng)事件層),如此說(shuō)來(lái)感覺(jué)太抽象,我們來(lái)看圖4-5-9就清楚了。
先從應(yīng)用程序的角度來(lái)認(rèn)識(shí)下input子系統(tǒng),在此可以從以下這個(gè)文件可以看到對(duì)應(yīng)的設(shè)備。打開(kāi)Linux終端,然后輸入cat ?/proc/bus/input/devices可以看到類(lèi)似以下的內(nèi)容。
1I:?Bus=0003?Vendor=046d?Product=c018?Version=0111 2N:?Name="?USB?Optical?Mouse" 3P:?Phys=usb-0000:00:1d.1-2/input0 4S:?Sysfs=/class/input/input24 5U:?Uniq= 6H:?Handlers=mouse1?event2 7B:?EV=7 8B:?KEY=70000?0?0?0?0?0?0?0?0 9B:?REL=103
這些devices主要是用來(lái)描述注冊(cè)在input子系統(tǒng)的一些設(shè)備文件,可能有鼠標(biāo),鍵盤(pán),觸摸屏,重力傳感器,溫度傳感器等等的設(shè)備,寫(xiě)驅(qū)動(dòng)的時(shí)候,通過(guò)內(nèi)核提供的input設(shè)備注冊(cè)設(shè)備相關(guān)的接口后,這些信息都會(huì)保存到對(duì)應(yīng)的文件里去。
那我們?nèi)绾蝸?lái)描述這樣的一個(gè)設(shè)備呢?Linux系統(tǒng)為我們提供了這個(gè)輸入系統(tǒng)操作相關(guān)的頭文件:#include
在這個(gè)文件中,我們可以找到這個(gè)結(jié)構(gòu)體:
1//用于描述一個(gè)輸入事件 2struct input_event { 3 struct timeval time; 4 __u16?type; 5 __u16?code; 6 __s32?value; 7};
在這其中,我們看到input_event這個(gè)結(jié)構(gòu)體中還有一個(gè)結(jié)構(gòu)體描述:struct timeval time;
先來(lái)解讀下這個(gè)結(jié)構(gòu)體的含義:
struct timeval結(jié)構(gòu)體在time.h中的定義如下
1struct timeval 2{ 3__time_t tv_sec; /*?Seconds.?*/ 4__suseconds_t tv_usec; /*Microseconds.?*/ 5};
其中,tv_sec為Epoch到創(chuàng)建struct timeval時(shí)的秒數(shù),tv_usec為微秒數(shù),即秒后面的零頭。type域顯示了被報(bào)告事件的類(lèi)型,例如,一個(gè) key press或者 button press, relative motion(比如移動(dòng)鼠標(biāo) )或者 absolute motion(比如移動(dòng)游戲桿 ); code域告訴你是哪一個(gè)key或者坐標(biāo)軸在被操作; value域告訴你現(xiàn)在的狀態(tài)或者運(yùn)動(dòng)情況是什么。
那么,最主要的事件有以下三種:相對(duì)事件(例如鼠標(biāo)),絕對(duì)事件(例如觸摸屏),鍵盤(pán)事件。
例如:我們說(shuō)說(shuō)鼠標(biāo),我們?cè)谝苿?dòng)鼠標(biāo)的時(shí)候鼠標(biāo)就是一個(gè)相對(duì)事件,所以type的類(lèi)型也就是底層上報(bào)給用戶的事件為相對(duì)事件類(lèi)型,那么code表示的就是相對(duì)于鼠標(biāo)當(dāng)前的位置的X或者Y的坐標(biāo),value也就是相對(duì)于當(dāng)前的位置偏移多少。
事件類(lèi)型(type)在input.h主要有以下:
1/* 2 *?Event?types 3 */ 4#define EV_SYN????????????0x00 //同步事件,就是將結(jié)果上報(bào)給系統(tǒng)的過(guò)程 5#define EV_KEY????????????0x01 //按鍵事件 6#define EV_REL????????????0x02 //相對(duì)事件 7#define EV_ABS????????????0x03 //絕對(duì)事件 8本節(jié),我們來(lái)實(shí)現(xiàn)一個(gè)input控制鼠標(biāo)的應(yīng)用程序。所以還會(huì)用到以下事件: 9/* 10 *?Relative?axes 11 */ 12//在這里,我們暫時(shí)只會(huì)用REL_X和REL_Y這兩個(gè)參數(shù) 13#define REL_X????????????0x00 //相對(duì)X坐標(biāo) 14#define REL_Y????????????0x01 //相對(duì)Y坐標(biāo)
那么value,就是選擇具體的type,具體的code以后所反應(yīng)出來(lái)的值,鼠標(biāo)就是相對(duì)于當(dāng)前X或者相對(duì)于當(dāng)前Y的值。
我們可以使用cat命令來(lái)測(cè)試當(dāng)前的鼠標(biāo)事件到底屬于哪一個(gè)事件節(jié)點(diǎn),如圖4-5-10所示:
只需切換到/dev/input下,找到對(duì)應(yīng)的事件節(jié)點(diǎn),然后使用cat eventx(事件節(jié)點(diǎn)),然后移動(dòng)鼠標(biāo)就可以看到數(shù)據(jù)打印啦,但是這些數(shù)據(jù)我們顯然是看不懂的,但我們可以使用一個(gè)測(cè)試程序?qū)⑹髽?biāo)的值讀出來(lái)。
接下來(lái),我們來(lái)看一下如何來(lái)讀取鼠標(biāo)事件,寫(xiě)一段代碼測(cè)試一下: mouse.c
1#include 2#include 3#include 4#include 5#include 6/* 7struct?input_event?{ 8 struct?timeval?time; 9 __u16?type; 10 __u16?code; 11 __s32?value; 12}; 13*/ 14/* 15Event?types 16#define?EV_SYN??????????????????0x00 17#define?EV_KEY??????????????????0x01 18#define?EV_REL??????????????????0x02 19#define?EV_ABS??????????????????0x03 20*/ 21/* 22 Relative?axes 23#define?REL_X???????????????????0x00 24#define?REL_Y???????????????????0x01 25#define?REL_Z???????????????????0x02 26#define?REL_MAX?????????????????0x0f 27#define?REL_CNT?????????????????(REL_MAX+1) 28*/ 29//event8??mouse 30//event9??keyboard 31int main(void) 32{ 33 //1、定義一個(gè)結(jié)構(gòu)體變量用來(lái)描述input事件 34 struct input_event event_mouse ; 35 //2、打開(kāi)input設(shè)備的事件節(jié)點(diǎn)我的電腦對(duì)應(yīng)的鼠標(biāo)事件的節(jié)點(diǎn)是event3 36//讀者的電腦的設(shè)備節(jié)點(diǎn)可能和我的不一樣,可以使用cat命令去獲取,然后 37//不斷嘗試 38 int fd?=?open("/dev/input/event4",O_RDWR); 39 int value?; 40 int type?; 41 int buffer[10]={0}; 42 if(-1 ==?fd){ 43 printf("open?mouse?event?fair!\n"); 44 return -1 ; 45 } 46 while(1){ 47 //3、讀事件 48 read(fd?,&event_mouse?,sizeof(event_mouse)); 49 //4、判斷事件類(lèi)型,并打印鍵碼 50 switch(event_mouse.type){ 51 //同步事件 52 case EV_SYN: 53 printf("sync!\n"); 54 break ; 55 case EV_REL: 56 //鼠標(biāo)事件,XY相對(duì)位移 57 //code表示相對(duì)位移X或者Y,當(dāng)判斷是X時(shí),打印X的相對(duì)位移value 58 //當(dāng)判斷是Y時(shí),打印Y的相對(duì)位移value 59 if(event_mouse.code?==?REL_X){ 60 printf("event_mouse.code_X:%d\n",event_mouse.code); 61 printf("event_mouse.value_X:%d\n",event_mouse.value); 62 } 63 if(event_mouse.code?==?REL_Y){ 64 printf("event_mouse.code_Y:%d\n",event_mouse.code); 65 printf("event_mouse.value_Y:%d\n",event_mouse.value); 66 } 67 defalut: 68 break ; 69 } 70 } 71 return 0 ; 72}
運(yùn)行結(jié)果,如圖4-5-11所示。
當(dāng)我們不斷移動(dòng)鼠標(biāo)的時(shí)候,這些值將會(huì)被打印出來(lái)。
請(qǐng)思考一個(gè)問(wèn)題,既然我們移動(dòng)鼠標(biāo)能夠打印數(shù)值,那我們能不能夠?qū)懸粋€(gè)程序控制鼠標(biāo)自動(dòng)移動(dòng)呢?那肯定是可以的,下面我們就讓鼠標(biāo)自己來(lái)畫(huà)一個(gè)正方形,上代碼:
1#include 2#include 3#include 4#include 5#include 6 7//event8??mouse 8//event9??keyboard 9int main(void) 10{ 11 //1、定義一個(gè)結(jié)構(gòu)體變量用來(lái)描述input事件 12 struct input_event event_mouse ; 13 //2、打開(kāi)input設(shè)備的事件節(jié)點(diǎn)??我的電腦鼠標(biāo)事件的節(jié)點(diǎn)是event3 14 int fd?=?open("/dev/input/event3",O_RDWR); 15 int value?; 16 int type?; 17 int i?; 18 int buffer[10]={0}; 19 if(-1 ==?fd){ 20 printf("open?mouse?event?fair!\n"); 21 return -1 ; 22 } 23 while(1){ 24 //3、寫(xiě)事件 25 for(i?= 0 ;?i?< 20 ;?i++){ 26 event_mouse.type?=?EV_REL?; 27 event_mouse.code?=?REL_X?; 28 event_mouse.value?=?i?; 29 write(fd,&event_mouse,sizeof(event_mouse)); 30 event_mouse.code?= 0 ; 31 event_mouse.value?= 0; 32 event_mouse.type?=?EV_SYN?; 33 write(fd,&event_mouse,sizeof(event_mouse)); 34 usleep(50000); 35 } 36 for(i?= 0 ;?i?< 20 ;?i++){ 37 event_mouse.type?=?EV_REL?; 38 event_mouse.code?=?REL_Y?; 39 event_mouse.value?=?i?; 40 write(fd,&event_mouse,sizeof(event_mouse)); 41 event_mouse.code?= 0 ; 42 event_mouse.value?= 0 ; 43 event_mouse.type?=?EV_SYN?; 44 write(fd,&event_mouse,sizeof(event_mouse)); 45 usleep(50000); 46 } 47 for(i?= 0 ;?i?> -20 ;?i--){ 48 event_mouse.type?=?EV_REL?; 49 event_mouse.code?=?REL_X?; 50 event_mouse.value?=?i?; 51 write(fd,&event_mouse,sizeof(event_mouse)); 52 event_mouse.code?= 0 ; 53 event_mouse.value?= 0; 54 event_mouse.type?=?EV_SYN?; 55 write(fd,&event_mouse,sizeof(event_mouse)); 56 usleep(50000); 57 } 58 for(i?= 0 ;?i?> -20 ;?i--){ 59 event_mouse.type?=?EV_REL?; 60 event_mouse.code?=?REL_Y?; 61 event_mouse.value?=?i?; 62 write(fd,&event_mouse,sizeof(event_mouse)); 63 event_mouse.code?= 0 ; 64 event_mouse.value?= 0 ; 65 event_mouse.type?=?EV_SYN?; 66 write(fd,&event_mouse,sizeof(event_mouse)); 67 usleep(50000); 68 } 69 70 } 71 return 0 ; 72}
執(zhí)行效果讀者自行驗(yàn)證!
接下來(lái)我們?cè)賹?xiě)一個(gè)案例,在Tiny4412平臺(tái)上獲取電容屏的坐標(biāo)值。
觸摸屏上報(bào)坐標(biāo)值的事件屬于絕對(duì)事件,也就是,觸摸的坐標(biāo)點(diǎn)X和Y會(huì)在屏幕的分辨率范圍內(nèi)上報(bào)一個(gè)絕對(duì)的坐標(biāo)(X,Y)。
那么上報(bào)對(duì)于的類(lèi)型(type)如下:EV_ABS
對(duì)于的code如下:
絕對(duì)于X:
ABS_MT_POSITION_X
絕對(duì)于Y:
ABS_MT_POSITION_Y
我用了一個(gè)程序獲取了屏幕的分辨率,得知分辨率寬為480,高為800。
首先,寫(xiě)這個(gè)程序時(shí),我通過(guò)adb進(jìn)到Android根目錄,然后用getevent -p查到觸摸屏的事件節(jié)點(diǎn)為event0,同時(shí)也知道觸摸屏是一個(gè)絕對(duì)事件,如下:
接下來(lái),我在Android5.0的源代碼external目錄下創(chuàng)建了如下目錄:Getft5x0x_Test
該目錄下有如下兩個(gè)文件文件:
Android.mk???? Get_ft5x0x_tp.c
(1)先看Android.mk
1LOCAL_PATH?:= $(call my-dir) 2include $(CLEAR_VARS) 3LOCAL_MODULE_TAGS?:=?eng 4LOCAL_SHARED_LIBRARIES?+=?libcutils?libutils 5#LOCAL_STATIC_LIBRARIES?+=?libz?libstdc++?libpng?libvtpng 6LOCAL_STATIC_LIBRARIES?+=?libz?libstdc++?libpng 7 8LOCAL_SRC_FILES?:=?Get_ft5x0x_tp.c 9LOCAL_MODULE?:=?ft5x0x_tp 10include $(BUILD_EXECUTABLE)
(2)Get_ft5x0x_tp.c
1#include 2#include 3#include 4#include 5#include 6#include 7#include 8#include 9#include 10#include 11#include 12#include 13#include 14#include 15#include 16#include 17//ft5x0x_ts觸摸屏事件初始化 18int touch_fd?= -1 ; 19int ft5x0x_ts__init(void) 20{ 21 touch_fd?=?open("/dev/input/event0",?O_RDONLY); 22 if (touch_fd?< 0) 23 { 24 printf("open?/dev/input/event0?failed\n"); 25 return -1; 26 } 27 return 0; 28} 29 30//獲取ft5x0x_ts觸摸屏上的坐標(biāo)點(diǎn) 31int Get_ft5x0x_ts_postion(int *x, int *y) 32{ 33 int touch_ret?= -1 ; 34 //1、定義一個(gè)結(jié)構(gòu)體變量用來(lái)描述ft5x0x觸摸屏事件 35 struct input_event ft5x0x_ts ; 36 37 //2、讀事件 38 touch_ret?=?read(touch_fd?,&ft5x0x_ts?,sizeof(ft5x0x_ts)); 39 if(touch_ret?< 0){ 40 printf("read?touch?fair!\n"); 41 } 42 //3、判斷事件類(lèi)型 43 switch(ft5x0x_ts.type) 44 { 45 case EV_SYN: 46 break ; 47 case EV_ABS: 48 if(ft5x0x_ts.code?==?ABS_MT_POSITION_X){ 49 *x?=?ft5x0x_ts.value?; 50 } 51 if(ft5x0x_ts.code?==?ABS_MT_POSITION_Y){ 52 *y?=?ft5x0x_ts.value?; 53 } 54 defalut: 55 break ; 56 } 57 return 0; 58} 59 60 61int main(int argc, char **argv) 62{ 63 int tp_ret?; 64 int ft5x0x_x?= 0; 65 int ft5x0x_y?= 0; 66 tp_ret?=?ft5x0x_ts__init(); 67 if(-1 ==?tp_ret){ 68 printf("tp?init?fair!\n"); 69 return -1 ; 70 } 71 printf("tp?init?success!\n"); 72 while(1) 73 { //獲取屏幕上的絕對(duì)坐標(biāo)點(diǎn) 74 Get_ft5x0x_ts_postion(&ft5x0x_x,&ft5x0x_y); 75 printf("ft5x0x_x:%d?????ft5x0x_y:%d\n",ft5x0x_x,ft5x0x_y); 76 usleep(100); 77 } 78 return 0; 79}
編寫(xiě)萬(wàn)makefile還有.c程序后:
我們切換到Android的根目錄下:
用以下命令編譯這個(gè)程序:
使用m,mm,mmm命令之前一定要先:
執(zhí)行:source和lunch這兩個(gè)步驟,如下:
1root@morixinguan:/work/android-5.0.2#?source?build/envsetup.sh 2including?device/samsung/manta/vendorsetup.sh 3including?device/moto/shamu/vendorsetup.sh 4including?device/friendly-arm/tiny4412/vendorsetup.sh 5including?device/generic/mini-emulator-x86_64/vendorsetup.sh 6including?device/generic/mini-emulator-armv7-a-neon/vendorsetup.sh 7including?device/generic/mini-emulator-mips/vendorsetup.sh 8including?device/generic/mini-emulator-arm64/vendorsetup.sh 9including?device/generic/mini-emulator-x86/vendorsetup.sh 10including?device/asus/deb/vendorsetup.sh 11including?device/asus/fugu/vendorsetup.sh 12including?device/asus/grouper/vendorsetup.sh 13including?device/asus/tilapia/vendorsetup.sh 14including?device/asus/flo/vendorsetup.sh 15including?device/lge/hammerhead/vendorsetup.sh 16including?device/lge/mako/vendorsetup.sh 17including?sdk/bash_completion/adb.bash 18root@morixinguan:/work/android-5.0.2#?lunch 19 20You're?building?on?Linux 21 22Lunch?menu...?pick?a?combo: 23 1.?aosp_arm-eng 24 2.?aosp_arm64-eng 25 3.?aosp_mips-eng 26 4.?aosp_mips64-eng 27 5.?aosp_x86-eng 28 6.?aosp_x86_64-eng 29 7.?aosp_manta-userdebug 30 8.?aosp_shamu-userdebug 31 9.?full_tiny4412-userdebug 32 10.?full_tiny4412-eng 33 11.?mini_emulator_x86_64-userdebug 34 12.?m_e_arm-userdebug 35 13.?mini_emulator_mips-userdebug 36 14.?mini_emulator_arm64-userdebug 37 15.?mini_emulator_x86-userdebug 38 16.?aosp_deb-userdebug 39 17.?full_fugu-userdebug 40 18.?aosp_fugu-userdebug 41 19.?aosp_grouper-userdebug 42 20.?aosp_tilapia-userdebug 43 21.?aosp_flo-userdebug 44 22.?aosp_hammerhead-userdebug 45 23.?aosp_mako-userdebug 46 47Which?would?you?like??[aosp_arm-eng]?20 48 49============================================ 50PLATFORM_VERSION_CODENAME=REL 51PLATFORM_VERSION=5.0.2 52TARGET_PRODUCT=aosp_tilapia 53TARGET_BUILD_VARIANT=userdebug 54TARGET_BUILD_TYPE=release 55TARGET_BUILD_APPS= 56TARGET_ARCH=arm 57TARGET_ARCH_VARIANT=armv7-a-neon 58TARGET_CPU_VARIANT=cortex-a9 59TARGET_2ND_ARCH= 60TARGET_2ND_ARCH_VARIANT= 61TARGET_2ND_CPU_VARIANT= 62HOST_ARCH=x86_64 63HOST_OS=linux 64HOST_OS_EXTRA=Linux-4.8.0-46-generic-x86_64-with-Ubuntu-16.04-xenial 65HOST_BUILD_TYPE=release 66BUILD_ID=LRX22G 67OUT_DIR=out 68============================================ 69 70root@morixinguan:/work/android-5.0.2#
接下來(lái),編譯程序:
mmm external/Getft5x0x_Test/
然后我們看到以下顯示:
這個(gè)二進(jìn)制生成的絕對(duì)路徑是在out目錄下,我們需要ft5x0x_tp這個(gè)文件,這個(gè)名字就是上面Android.mk里面對(duì)應(yīng)的:
LOCAL_MODULE := ft5x0x_tp
Install: out/target/product/tiny4412/system/bin/ft5x0x_tp
將這個(gè)文件拷貝到當(dāng)前目錄下:
cp out/target/product/tiny4412/system/bin/ft5x0x_tp .
然后,用USB線連接你的開(kāi)發(fā)板,然后用adb命令將ft5x0x_tp ?push到system/bin/目錄下,這個(gè)目錄是Android的根文件系統(tǒng)下的一個(gè)命令,很多命令都在這個(gè)目錄下。
如果沒(méi)有安裝adb,可以apt-get install adb 安裝adb
adb push完畢以后:
我們?cè)诮K端: adb shell切換到根目錄下:
執(zhí)行ft5x0x_tp這個(gè)bin文件,然后我們觸摸觸摸屏,如下,坐標(biāo)值打印出來(lái)了。
長(zhǎng)期商務(wù)合作服務(wù):
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!