大家好,今天主要和大家聊一聊,如何使用标準輸入裝置,進行控制資訊的識别。

目錄
第一:按鍵應用程式設計方法
第二:單點觸摸應用程式實作
第三:多點觸摸應用程式實作
第一:按鍵應用程式設計方法
編寫一個應用程式,擷取按鍵狀态,判斷按鍵目前是按下,松開或長按狀态。
#以字母A鍵為例
KEY_A //上報KEY_A事件
SYN_REPORT //同步
如果是按下,則上報KEY_A事件時,value=1;如果是松開,則value=0;如果長按,則value=2。接下來編寫按鈕應用程式,讀取按鍵狀态并将結果列印出來,代碼如下所示。
#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 in_ev = {0};
int fd = -1;
int value = -1;
/* 校驗傳參 */
if (2 != argc) {
fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
exit(-1);
}
/* 打開檔案 */
if (0 > (fd = open(argv[1], O_RDONLY))) {
perror("open error");
exit(-1);
}
for ( ; ; ) {
/* 循環讀取資料 */
if (sizeof(struct input_event) !=
read(fd, &in_ev, sizeof(struct input_event))) {
perror("read error");
exit(-1);
}
if (EV_KEY == in_ev.type) { //按鍵事件
switch (in_ev.value) {
case 0:
printf("code<%d>: 松開\n", in_ev.code);
break;
case 1:
printf("code<%d>: 按下\n", in_ev.code);
break;
case 2:
printf("code<%d>: 長按\n", in_ev.code);
break;
}
}
}
}
在for循環中,調用read()讀取輸入裝置上報的資料,當按鍵按下或松開(以及長按)動作發生時,read()會讀取到輸入裝置上報的資料,首先判斷此次上報的事件是否是按鍵類事件(EV_KEY),如果是按鍵類事件,接着根據value值來判斷按鍵目前的狀态是松開、按下還是長按。
将編譯得到的可執行檔案複制到開發闆Linux系統的家目錄下:
注意:除了能夠測試KEY0按鍵之外,還可以測試鍵盤上的按鍵,可以找到一個USB鍵盤連接配接到開發闆的USB HOST接口上,當鍵盤插入之後,終端将會列印出相應的驅動加載資訊。
驅動加載成功之後,可以檢視下該鍵盤裝置對應的裝置節點,使用指令"cat /proc/bus/input/devices",在列印資訊中找到鍵盤裝置的資訊:
操作的時候,可以對應相應的裝置節點/dev/input/event3,運作測試程式并按下、松開鍵盤上的按鍵;
大家可以根據code值查詢對應的按鍵,譬如code=30對應的鍵盤上的字母A鍵,code=48對應的字母B鍵。
第二:單點觸摸應用程式實作
通過上面的詳細介紹,大家應該知道如何編寫一個觸摸屏的應用程式了,接下來我們編寫一個單點觸摸屏應用程式,擷取一個觸摸點的坐标資訊,并将其列印出來。具體代碼實作如下:
#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 in_ev;
int x, y; //觸摸點 x 和 y 坐标
int down; //用于記錄 BTN_TOUCH 事件的 value,1 表示按下,0 表示松開,-1 表示移動
int valid; //用于記錄資料是否有效(我們關注的資訊發生更新表示有效,1 表示有效,0 表示無效)
int fd = -1;
/* 校驗傳參 */
if (2 != argc) {
fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
exit(EXIT_FAILURE);
}
/* 打開檔案 */
if (0 > (fd = open(argv[1], O_RDONLY))) {
perror("open error");
exit(EXIT_FAILURE);
}
x = y = 0; //初始化 x 和 y 坐标值
down = -1; //初始化<移動>
valid = 0;//初始化<無效>
for ( ; ; ) {
/* 循環讀取資料 */
if (sizeof(struct input_event) !=
read(fd, &in_ev, sizeof(struct input_event))) {
perror("read error");
exit(EXIT_FAILURE);
}
switch(in_ev.type) {
case EV_KEY: //按鍵事件
if (BTN_TOUCH == in_ev.code) {
down = in_ev.value;
valid = 1;
}
break;
case EV_ABS: //絕對位移事件
switch (in_ev.code) {
case ABS_X: //X 坐标
x = in_ev.value;
valid = 1;
break;
case ABS_Y: //Y 坐标
y = in_ev.value;
valid = 1;
break;
}
break;
case EV_SYN: //同步事件
if (SYN_REPORT == in_ev.code) {
if (valid) {//判斷是否有效
switch (down) {//判斷狀态
case 1:
printf("按下(%d, %d)\n", x, y);
break;
case 0:
printf("松開\n");
break;
case -1:
printf("移動(%d, %d)\n", x, y);
break;
}
valid = 0; //重置 valid
down = -1; //重置 down
}
}
break;
}
}
}
分析:程式中先傳入參數,main()函數中定義了4個變量;
⑴、變量 x 表示觸摸點的 X 坐标;
⑵、變量 y 表示觸摸點的 Y 坐标;
⑶、變量 down 表示手指狀态時候按下、松開還是滑動,down=1 表示手指按下、down=0 表示手指松開、down=-1 表示手指滑動;
⑷、變量 valid 表示資料是否有效,valid=1 表示有效、valid=0 表示無效;有效指的是我們檢測的資訊發生了更改,譬如程式中隻檢測了手指的按下、松開動作以及坐标值的變化。接着調用 open()打開觸摸屏裝置檔案得到檔案描述符 fd;在 for 循環之前,首先對 x、y、down、valid這 4 個變量進行初始化操作。在 for 循環讀取觸摸屏上報的資料,将讀取到的資料存放在 struct input_event資料結構中。在 switch…case 語句中對讀取到的資料進行解析,擷取 BTN_TOUCH 事件的 value 資料,判斷觸摸屏是按下還是松開狀态,擷取 ABS_X 和 ABS_Y 事件的 value 變量,得到觸摸點的 X 軸坐标和 Y 軸坐标。
當上報同步事件時,表示資料已近完整,接着對得到的資料進行分析,列印坐标資訊。
第三:多點觸摸應用程式實作
實作了單點觸摸應用程式之後,可以再來實作多點觸摸屏應用程式該如何實作。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
/* 用于描述 MT 多點觸摸每一個觸摸點的資訊 */
struct ts_mt {
int x; //X 坐标
int y; //Y 坐标
int id; //對應 ABS_MT_TRACKING_ID
int valid; //資料有效标志位(=1 表示觸摸點資訊發生更新)
};
/* 一個觸摸點的 x 坐标和 y 坐标 */
struct tp_xy {
int x;
int y;
};
static int ts_read(const int fd, const int max_slots,
struct ts_mt *mt)
{
struct input_event in_ev;
static int slot = 0;//用于儲存上一個 slot
static struct tp_xy xy[12] = {0};//用于儲存上一次的 x 和 y 坐标值,假設觸摸屏支援的最大觸摸點數不會超
過 12
int i;
/* 對緩沖區初始化操作 */
memset(mt, 0x0, max_slots * sizeof(struct ts_mt)); //清零
for (i = 0; i < max_slots; i++)
mt[i].id = -2;//将 id 初始化為-2, id=-1 表示觸摸點删除, id>=0 表示建立
for ( ; ; ) {
if (sizeof(struct input_event) !=
read(fd, &in_ev, sizeof(struct input_event))) {
perror("read error");
return -1;
}
switch (in_ev.type) {
case EV_ABS:
switch (in_ev.code) {
case ABS_MT_SLOT:
slot = in_ev.value;
break;
case ABS_MT_POSITION_X:
xy[slot].x = in_ev.value;
mt[slot].valid = 1;
break;
case ABS_MT_POSITION_Y:
xy[slot].y = in_ev.value;
mt[slot].valid = 1;
break;
case ABS_MT_TRACKING_ID:
mt[slot].id = in_ev.value;
mt[slot].valid = 1;
break;
}
break;
//case EV_KEY://按鍵事件對單點觸摸應用比較有用
// break;
case EV_SYN:
if (SYN_REPORT == in_ev.code) {
for (i = 0; i < max_slots; i++) {
mt[i].x = xy[i].x;
mt[i].y = xy[i].y;
}
}
return 0;
}
}
}
int main(int argc, char *argv[])
{
struct input_absinfo slot;
struct ts_mt *mt = NULL;
int max_slots;
int fd;
int i;
/* 參數校驗 */
if (2 != argc) {
fprintf(stderr,"usage: %s <input_dev>\n", argv[0]);
exit(EXIT_FAILURE);
}
/* 打開檔案 */
fd = open(argv[1], O_RDONLY);
if (0 > fd) {
perror("open error");
exit(EXIT_FAILURE);
}
/* 擷取觸摸屏支援的最大觸摸點數 */
if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)) {
perror("ioctl error");
close(fd);
exit(EXIT_FAILURE);
}
max_slots = slot.maximum + 1 - slot.minimum;
printf("max_slots: %d\n", max_slots);
/* 申請記憶體空間并清零 */
mt = calloc(max_slots, sizeof(struct ts_mt));
/* 讀資料 */
for ( ; ; ) {
if (0 > ts_read(fd, max_slots, mt))
break;
for (i = 0; i < max_slots; i++) {
if (mt[i].valid) {//判斷每一個觸摸點資訊是否發生更新(關注的資訊發生更新)
if (0 <= mt[i].id)
printf("slot<%d>, 按下(%d, %d)\n", i, mt[i].x, mt[i].y);
else if (-1 == mt[i].id)
printf("slot<%d>, 松開\n", i);
else
printf("slot<%d>, 移動(%d, %d)\n", i, mt[i].x, mt[i].y);
}
}
}
/* 關閉裝置、退出 */
close(fd);
free(mt);
exit(EXIT_FAILURE);
}
示例代碼中申明了 struct ts_mt 資料結構,用于描述多點觸摸情況下每一個觸摸點的資訊。
首先來看下 main()函數,定義了 max_slots 變量,用于指定觸摸屏裝置的支援的最大觸摸點數,通過:
ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)
擷取到觸摸屏該資訊。
接着根據 max_slots 變量的值,為 mt 指針申請記憶體:
mt = calloc(max_slots, sizeof(struct ts_mt));
for( ; ; )循環中調用 ts_read()函數,該函數是自定義函數,用于擷取觸摸屏上報的資料,第一個參數表示檔案描述符 fd、第二個參數表示觸摸屏支援的最大觸摸點數、第三個參數則是 struct ts_mt 數組,ts_read()函數會将擷取到的資料存放在數組中,mt[0]表示 slot<0>資料、mt[1]表示 slot<1>的資料依次類推!
在内部的 for 循環中,則對擷取到的資料進行分析,判斷資料是否有效,并根據 id 判斷手指的動作,在單點觸摸應用程式中,我們是通過 BTN_TOUCH 事件來判斷手指的動作;而在多點觸摸應用中,我們需要通過 id 來判斷多個手指的動作。
關于自定義函數 ts_read()就不再介紹了,代碼的注釋已經描述很清楚了!